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"); });