316 lines
8.8 KiB
JavaScript
316 lines
8.8 KiB
JavaScript
|
const express = require("express");
|
||
|
const bcrypt = require("bcryptjs");
|
||
|
const jwt = require("jsonwebtoken");
|
||
|
const { PrismaClient } = require("@prisma/client");
|
||
|
const crypto = require("crypto");
|
||
|
const cors = require("cors");
|
||
|
require("dotenv").config();
|
||
|
|
||
|
const prisma = new PrismaClient();
|
||
|
const app = express();
|
||
|
app.use(express.json());
|
||
|
|
||
|
|
||
|
app.use(cors({
|
||
|
origin: "*", // Allow all origins
|
||
|
methods: ["GET", "POST", "PUT", "DELETE"], // Allow all methods
|
||
|
}));
|
||
|
|
||
|
const JWT_SECRET = "your_jwt_secret"; // Replace with a secure secret
|
||
|
|
||
|
// Authentication Middleware
|
||
|
const authenticate = async (req, res, next) => {
|
||
|
const token = req.header("Authorization")?.replace("Bearer ", "");
|
||
|
if (!token) return res.status(401).send("Access denied");
|
||
|
|
||
|
try {
|
||
|
const decoded = jwt.verify(token, JWT_SECRET);
|
||
|
req.user = decoded;
|
||
|
next();
|
||
|
} catch (err) {
|
||
|
return res.status(401).send("Invalid token");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Register User
|
||
|
app.post("/register", async (req, res) => {
|
||
|
const { email, username, password, pubKey } = req.body;
|
||
|
|
||
|
const userExists = await prisma.user.findUnique({ where: { username } });
|
||
|
if (userExists) return res.status(400).send("User already exists");
|
||
|
|
||
|
const hashedPassword = await bcrypt.hash(password, 10);
|
||
|
const user = await prisma.user.create({
|
||
|
data: {
|
||
|
email,
|
||
|
username,
|
||
|
password: hashedPassword,
|
||
|
pubKey,
|
||
|
},
|
||
|
});
|
||
|
|
||
|
const token = jwt.sign({ username: user.username }, JWT_SECRET, { expiresIn: "1h" });
|
||
|
res.send({ token });
|
||
|
});
|
||
|
|
||
|
// Update Public Key
|
||
|
app.post("/user/updatePublicKey", authenticate, async (req, res) => {
|
||
|
const { pubKey } = req.body;
|
||
|
const username = req.user.username;
|
||
|
|
||
|
if (!pubKey) return res.status(400).send("Public key is required");
|
||
|
|
||
|
try {
|
||
|
await prisma.user.update({
|
||
|
where: { username },
|
||
|
data: { pubKey },
|
||
|
});
|
||
|
|
||
|
res.send({ message: "Public key updated successfully" });
|
||
|
} catch (error) {
|
||
|
console.error("Error updating public key:", error);
|
||
|
res.status(500).send({ error: "Failed to update public key" });
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
// Login User
|
||
|
// Login User
|
||
|
app.post("/login", async (req, res) => {
|
||
|
const { username, password } = req.body;
|
||
|
|
||
|
const user = await prisma.user.findUnique({
|
||
|
where: { username },
|
||
|
select: { password: true, pubKey: true },
|
||
|
});
|
||
|
if (!user) return res.status(400).send("Invalid credentials");
|
||
|
|
||
|
const isMatch = await bcrypt.compare(password, user.password);
|
||
|
if (!isMatch) return res.status(400).send("Invalid credentials");
|
||
|
|
||
|
const token = jwt.sign({ username }, JWT_SECRET, { expiresIn: "1h" });
|
||
|
res.send({ token, pubKey: user.pubKey });
|
||
|
});
|
||
|
|
||
|
|
||
|
// Fetch pending friend requests sent to the authenticated user
|
||
|
app.get("/friend-requests/pending", authenticate, async (req, res) => {
|
||
|
const username = req.user.username;
|
||
|
|
||
|
try {
|
||
|
const pendingRequests = await prisma.friendRequest.findMany({
|
||
|
where: {
|
||
|
receiverUsername: username,
|
||
|
status: "pending",
|
||
|
},
|
||
|
include: {
|
||
|
sender: {
|
||
|
select: { username: true },
|
||
|
},
|
||
|
},
|
||
|
});
|
||
|
|
||
|
res.send(pendingRequests);
|
||
|
} catch (err) {
|
||
|
res.status(500).send({ error: "Failed to fetch pending friend requests" });
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Send Friend Request
|
||
|
app.post("/friend-request", authenticate, async (req, res) => {
|
||
|
const { receiverUsername } = req.body;
|
||
|
const senderUsername = req.user.username;
|
||
|
|
||
|
const receiver = await prisma.user.findUnique({ where: { username: receiverUsername } });
|
||
|
if (!receiver) return res.status(404).send("User not found");
|
||
|
|
||
|
const existingRequest = await prisma.friendRequest.findFirst({
|
||
|
where: {
|
||
|
OR: [
|
||
|
{ senderUsername, receiverUsername },
|
||
|
{ senderUsername: receiverUsername, receiverUsername: senderUsername },
|
||
|
],
|
||
|
},
|
||
|
});
|
||
|
|
||
|
if (existingRequest) return res.status(400).send("Friend request already exists");
|
||
|
|
||
|
const request = await prisma.friendRequest.create({
|
||
|
data: {
|
||
|
senderUsername,
|
||
|
receiverUsername,
|
||
|
status: "pending",
|
||
|
},
|
||
|
});
|
||
|
|
||
|
res.send(request);
|
||
|
});
|
||
|
|
||
|
// Respond to Friend Request
|
||
|
app.post("/friend-request/respond", authenticate, async (req, res) => {
|
||
|
const { requestId, response } = req.body;
|
||
|
const username = req.user.username;
|
||
|
|
||
|
const request = await prisma.friendRequest.findUnique({ where: { id: requestId } });
|
||
|
if (!request || request.receiverUsername !== username)
|
||
|
return res.status(403).send("You cannot respond to this request");
|
||
|
|
||
|
const status = response === "accepted" ? "accepted" : "rejected";
|
||
|
|
||
|
await prisma.friendRequest.update({
|
||
|
where: { id: requestId },
|
||
|
data: { status },
|
||
|
});
|
||
|
|
||
|
res.send("Response recorded");
|
||
|
});
|
||
|
|
||
|
// Fetch Friends
|
||
|
app.get("/friends", authenticate, async (req, res) => {
|
||
|
const username = req.user.username;
|
||
|
|
||
|
try {
|
||
|
const friends = await prisma.friendRequest.findMany({
|
||
|
where: {
|
||
|
OR: [
|
||
|
{ senderUsername: username, status: "accepted" },
|
||
|
{ receiverUsername: username, status: "accepted" },
|
||
|
],
|
||
|
},
|
||
|
include: {
|
||
|
sender: { select: { username: true, email: true } },
|
||
|
receiver: { select: { username: true, email: true } },
|
||
|
},
|
||
|
});
|
||
|
|
||
|
const friendList = friends.map((f) => ({
|
||
|
username: f.senderUsername === username ? f.receiver.username : f.sender.username,
|
||
|
email: f.senderUsername === username ? f.receiver.email : f.sender.email,
|
||
|
}));
|
||
|
|
||
|
res.send(friendList);
|
||
|
} catch (err) {
|
||
|
res.status(500).send({ error: "Failed to fetch friends" });
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Fetch Messages Between Two Users
|
||
|
app.get("/messages", authenticate, async (req, res) => {
|
||
|
const { receiverUsername } = req.query;
|
||
|
const username = req.user.username;
|
||
|
|
||
|
if (!receiverUsername) {
|
||
|
return res.status(400).send("Receiver username is required");
|
||
|
}
|
||
|
|
||
|
const messages = await prisma.message.findMany({
|
||
|
where: {
|
||
|
OR: [
|
||
|
{ senderUsername: username, receiverUsername },
|
||
|
{ senderUsername: receiverUsername, receiverUsername: username },
|
||
|
],
|
||
|
},
|
||
|
orderBy: { createdAt: "asc" },
|
||
|
});
|
||
|
|
||
|
res.send(messages);
|
||
|
});
|
||
|
|
||
|
|
||
|
// Send a Message
|
||
|
app.post("/messages", authenticate, async (req, res) => {
|
||
|
const senderUsername = req.user.username;
|
||
|
const { encrypted, nonce, pubKey, receiverUsername } = req.body;
|
||
|
console.log(encrypted)
|
||
|
if (!encrypted || !nonce || !pubKey || !receiverUsername) {
|
||
|
return res.status(400).send("Missing required fields (encrypted, nonce, pubKey, receiverUsername)");
|
||
|
}
|
||
|
|
||
|
const receiver = await prisma.user.findUnique({ where: { username: receiverUsername } });
|
||
|
if (!receiver) return res.status(404).send("Receiver not found");
|
||
|
|
||
|
const isFriend = await prisma.friendRequest.findFirst({
|
||
|
where: {
|
||
|
OR: [
|
||
|
{ senderUsername, receiverUsername, status: "accepted" },
|
||
|
{ senderUsername: receiverUsername, receiverUsername: senderUsername, status: "accepted" },
|
||
|
],
|
||
|
},
|
||
|
});
|
||
|
|
||
|
if (!isFriend) return res.status(403).send("You can only message friends");
|
||
|
|
||
|
const message = await prisma.message.create({
|
||
|
data: {
|
||
|
content: encrypted,
|
||
|
senderUsername,
|
||
|
receiverUsername,
|
||
|
nonce: nonce,
|
||
|
pubKey: pubKey,
|
||
|
},
|
||
|
});
|
||
|
|
||
|
res.send(message);
|
||
|
});
|
||
|
|
||
|
app.get("/test" , async(req,res) => {
|
||
|
const messages = await prisma.message.findMany()
|
||
|
res.send(messages)
|
||
|
})
|
||
|
|
||
|
|
||
|
// Get User Public Key if they are friends
|
||
|
app.get("/user/pubkey", authenticate, async (req, res) => {
|
||
|
const { username: friendUsername } = req.query; // Username of the requested friend
|
||
|
const username = req.user.username; // Authenticated user's username
|
||
|
|
||
|
try {
|
||
|
// Check if the user exists
|
||
|
const user = await prisma.user.findUnique({
|
||
|
where: { username: friendUsername },
|
||
|
select: { pubKey: true }, // Only select the public key
|
||
|
});
|
||
|
|
||
|
if (!user) {
|
||
|
return res.status(404).send("User not found");
|
||
|
}
|
||
|
|
||
|
// Check if the authenticated user is friends with the requested user
|
||
|
const isFriend = await prisma.friendRequest.findFirst({
|
||
|
where: {
|
||
|
OR: [
|
||
|
{ senderUsername: username, receiverUsername: friendUsername, status: "accepted" },
|
||
|
{ senderUsername: friendUsername, receiverUsername: username, status: "accepted" },
|
||
|
],
|
||
|
},
|
||
|
});
|
||
|
|
||
|
if (!isFriend) {
|
||
|
return res.status(403).send("You can only fetch the public key of your friends");
|
||
|
}
|
||
|
|
||
|
// Return the public key
|
||
|
res.send({ pubKey: user.pubKey });
|
||
|
} catch (err) {
|
||
|
res.status(500).send({ error: "Failed to fetch the public key" });
|
||
|
}
|
||
|
});
|
||
|
|
||
|
|
||
|
// Delete Message
|
||
|
app.delete("/messages/:id", authenticate, async (req, res) => {
|
||
|
const { id } = req.params;
|
||
|
const username = req.user.username;
|
||
|
|
||
|
const message = await prisma.message.findUnique({ where: { id: parseInt(id) } });
|
||
|
if (!message || message.senderUsername !== username)
|
||
|
return res.status(403).send("You can only delete your own messages");
|
||
|
|
||
|
await prisma.message.delete({ where: { id: parseInt(id) } });
|
||
|
res.send("Message deleted successfully");
|
||
|
});
|
||
|
|
||
|
app.listen(4000, () => {
|
||
|
console.log("Server running on http://localhost:4000");
|
||
|
});
|