242 lines
7.4 KiB
JavaScript
242 lines
7.4 KiB
JavaScript
const express = require("express");
|
|
const router = express.Router();
|
|
const pool = require("../../db");
|
|
const driver = require("../../graph_db");
|
|
const { strictInput } = require("../../middleware/strict_input");
|
|
const { authenticateToken } = require("../../middleware/authenticate_token");
|
|
const { graphIdToNumber } = require("./friend_graph");
|
|
|
|
const GRAPH = { database: process.env.GRAPH_DB_NAME };
|
|
|
|
function validId(value) {
|
|
return Number.isInteger(value) && value > 0;
|
|
}
|
|
|
|
async function personIdsFromGraph(records) {
|
|
return records.map((record) => graphIdToNumber(record.get("id")));
|
|
}
|
|
|
|
async function personDetails(ids) {
|
|
if (ids.length === 0) return [];
|
|
const { rows } = await pool.query(
|
|
"SELECT id, name, username, profile_link, private FROM people WHERE id = ANY($1) ORDER BY name",
|
|
[ids],
|
|
);
|
|
return rows;
|
|
}
|
|
|
|
async function sendRequest(req, res) {
|
|
const me = Number(req.user.id);
|
|
const to = Number(req.query.to);
|
|
if (!validId(to)) {
|
|
return res.status(400).send("Invalid user id");
|
|
}
|
|
if (me === to) {
|
|
return res.status(400).send("You cannot send a friend request to yourself");
|
|
}
|
|
|
|
try {
|
|
const target = await pool.query("SELECT id FROM people WHERE id = $1", [to]);
|
|
if (target.rowCount === 0) {
|
|
return res.status(404).send("User not found");
|
|
}
|
|
|
|
const result = await driver.executeQuery(
|
|
`MATCH (me:Person {db_id: $me}), (them:Person {db_id: $them})
|
|
WHERE NOT EXISTS((me)-[:FRIENDS_WITH]-(them))
|
|
AND NOT EXISTS((me)-[:REQUESTED]->(them))
|
|
AND NOT EXISTS((them)-[:REQUESTED]->(me))
|
|
CREATE (me)-[:REQUESTED {created_at: datetime()}]->(them)
|
|
RETURN me`,
|
|
{ me, them: to },
|
|
GRAPH,
|
|
);
|
|
if (result.records.length === 0) {
|
|
return res.status(409).send("Request already exists or you are already friends");
|
|
}
|
|
return res.status(200).json({ message: "Friend request sent" });
|
|
} catch (err) {
|
|
console.error("friend request failed", {
|
|
message: err.message,
|
|
code: err.code,
|
|
detail: err.detail,
|
|
hint: err.hint,
|
|
});
|
|
return res.status(500).send("Friend request was not sent, request failed");
|
|
}
|
|
}
|
|
|
|
async function acceptRequest(req, res) {
|
|
const me = Number(req.user.id);
|
|
const from = Number(req.query.from);
|
|
if (!validId(from)) {
|
|
return res.status(400).send("Invalid user id");
|
|
}
|
|
|
|
try {
|
|
const result = await driver.executeQuery(
|
|
`MATCH (f:Person {db_id: $from})-[r:REQUESTED]->(me:Person {db_id: $me})
|
|
DELETE r
|
|
CREATE (f)-[:FRIENDS_WITH]->(me)
|
|
CREATE (me)-[:FRIENDS_WITH]->(f)
|
|
RETURN f`,
|
|
{ me, from },
|
|
GRAPH,
|
|
);
|
|
if (result.records.length === 0) {
|
|
return res.status(404).send("No pending friend request from this user");
|
|
}
|
|
return res.status(200).json({ message: "Friend request accepted" });
|
|
} catch (err) {
|
|
console.error("friend request accept failed", {
|
|
message: err.message,
|
|
code: err.code,
|
|
detail: err.detail,
|
|
hint: err.hint,
|
|
});
|
|
return res.status(500).send("Friend request was not accepted, request failed");
|
|
}
|
|
}
|
|
|
|
async function declineRequest(req, res) {
|
|
const me = Number(req.user.id);
|
|
const from = Number(req.query.from);
|
|
if (!validId(from)) {
|
|
return res.status(400).send("Invalid user id");
|
|
}
|
|
|
|
try {
|
|
const result = await driver.executeQuery(
|
|
`MATCH (f:Person {db_id: $from})-[r:REQUESTED]->(me:Person {db_id: $me})
|
|
DELETE r
|
|
RETURN f`,
|
|
{ me, from },
|
|
GRAPH,
|
|
);
|
|
if (result.records.length === 0) {
|
|
return res.status(404).send("No pending friend request from this user");
|
|
}
|
|
return res.status(200).json({ message: "Friend request declined" });
|
|
} catch (err) {
|
|
console.error("friend request decline failed", {
|
|
message: err.message,
|
|
code: err.code,
|
|
detail: err.detail,
|
|
hint: err.hint,
|
|
});
|
|
return res.status(500).send("Friend request was not declined, request failed");
|
|
}
|
|
}
|
|
|
|
async function cancelRequest(req, res) {
|
|
const me = Number(req.user.id);
|
|
const to = Number(req.query.to);
|
|
if (!validId(to)) {
|
|
return res.status(400).send("Invalid user id");
|
|
}
|
|
|
|
try {
|
|
const result = await driver.executeQuery(
|
|
`MATCH (me:Person {db_id: $me})-[r:REQUESTED]->(t:Person {db_id: $to})
|
|
DELETE r
|
|
RETURN t`,
|
|
{ me, to },
|
|
GRAPH,
|
|
);
|
|
if (result.records.length === 0) {
|
|
return res.status(404).send("No pending friend request to this user");
|
|
}
|
|
return res.status(200).json({ message: "Friend request cancelled" });
|
|
} catch (err) {
|
|
console.error("friend request cancel failed", {
|
|
message: err.message,
|
|
code: err.code,
|
|
detail: err.detail,
|
|
hint: err.hint,
|
|
});
|
|
return res.status(500).send("Friend request was not cancelled, request failed");
|
|
}
|
|
}
|
|
|
|
async function listFriends(req, res) {
|
|
try {
|
|
const { records } = await driver.executeQuery(
|
|
"MATCH (me:Person {db_id: $me})-[:FRIENDS_WITH]-(f:Person) RETURN f.db_id AS id",
|
|
{ me: Number(req.user.id) },
|
|
GRAPH,
|
|
);
|
|
const ids = await personIdsFromGraph(records);
|
|
const friends = await personDetails(ids);
|
|
return res.status(200).json(friends);
|
|
} catch (err) {
|
|
console.error("list friends failed", {
|
|
message: err.message,
|
|
code: err.code,
|
|
detail: err.detail,
|
|
hint: err.hint,
|
|
});
|
|
return res.status(500).send("Friends could not be retrieved, request failed");
|
|
}
|
|
}
|
|
|
|
async function listRequests(req, res) {
|
|
try {
|
|
const { records } = await driver.executeQuery(
|
|
"MATCH (f:Person)-[:REQUESTED]->(me:Person {db_id: $me}) RETURN f.db_id AS id",
|
|
{ me: Number(req.user.id) },
|
|
GRAPH,
|
|
);
|
|
const ids = await personIdsFromGraph(records);
|
|
const requesters = await personDetails(ids);
|
|
return res.status(200).json(requesters);
|
|
} catch (err) {
|
|
console.error("list friend requests failed", {
|
|
message: err.message,
|
|
code: err.code,
|
|
detail: err.detail,
|
|
hint: err.hint,
|
|
});
|
|
return res.status(500).send("Friend requests could not be retrieved, request failed");
|
|
}
|
|
}
|
|
|
|
async function removeFriend(req, res) {
|
|
const me = Number(req.user.id);
|
|
const id = Number(req.query.id);
|
|
if (!validId(id)) {
|
|
return res.status(400).send("Invalid user id");
|
|
}
|
|
|
|
try {
|
|
const result = await driver.executeQuery(
|
|
`MATCH (me:Person {db_id: $me})-[r:FRIENDS_WITH]-(f:Person {db_id: $id})
|
|
DELETE r
|
|
RETURN f`,
|
|
{ me, id },
|
|
GRAPH,
|
|
);
|
|
if (result.records.length === 0) {
|
|
return res.status(404).send("Friend not found or not in your friend list");
|
|
}
|
|
return res.status(200).json({ message: "Friend removed" });
|
|
} catch (err) {
|
|
console.error("friend removal failed", {
|
|
message: err.message,
|
|
code: err.code,
|
|
detail: err.detail,
|
|
hint: err.hint,
|
|
});
|
|
return res.status(500).send("Friend could not be removed, request failed");
|
|
}
|
|
}
|
|
|
|
router.get("/", authenticateToken, strictInput(), listFriends);
|
|
router.get("/requests", authenticateToken, strictInput(), listRequests);
|
|
router.post("/request", authenticateToken, strictInput({ query: ["to"] }), sendRequest);
|
|
router.post("/accept", authenticateToken, strictInput({ query: ["from"] }), acceptRequest);
|
|
router.post("/decline", authenticateToken, strictInput({ query: ["from"] }), declineRequest);
|
|
router.post("/cancel", authenticateToken, strictInput({ query: ["to"] }), cancelRequest);
|
|
router.delete("/remove", authenticateToken, strictInput({ query: ["id"] }), removeFriend);
|
|
|
|
module.exports = router;
|