Added liking and security measures
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
var driver = require("../../graph_db");
|
||||
const { strictInput } = require('../../middleware/strict_input');
|
||||
|
||||
async function create_friendship(req, res) {
|
||||
try {
|
||||
const me = req.query.me;
|
||||
const them = req.query.them;
|
||||
if (String(req.user.id) !== String(me)) {
|
||||
return res.status(403).send("You can only create relationships as yourself");
|
||||
}
|
||||
const { records } = await driver.executeQuery(
|
||||
"MATCH (m:Person {db_id: $me}) MATCH (t:Person {db_id: $them}) CREATE (m)-[:FRIENDS_WITH]-> (t) CREATE (t)-[:FRIENDS_WITH]-> (m)",
|
||||
{ me: me, them: them },
|
||||
{ database: process.env.GRAPH_DB_NAME },
|
||||
);
|
||||
res.status(200).send("Relationship has been created");
|
||||
} catch (err) {
|
||||
console.error("database query failed", {
|
||||
message: err.message,
|
||||
code: err.code,
|
||||
detail: err.detail,
|
||||
hint: err.hint,
|
||||
stack: err.stack,
|
||||
});
|
||||
res.status(500).send("Relationship was not created, request failed");
|
||||
}
|
||||
}
|
||||
|
||||
router.post("/", strictInput({ query: ['me', 'them'] }), create_friendship);
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,241 @@
|
||||
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;
|
||||
@@ -0,0 +1,39 @@
|
||||
const driver = require("../../graph_db");
|
||||
|
||||
function graphIdToNumber(value) {
|
||||
if (typeof value === "number") return value;
|
||||
if (value && typeof value.toNumber === "function") return value.toNumber();
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
function graphParams(userId) {
|
||||
return { db_id: Number(userId) };
|
||||
}
|
||||
|
||||
async function getFriendIds(userId) {
|
||||
const { records } = await driver.executeQuery(
|
||||
"MATCH (me:Person {db_id: $db_id})-[:FRIENDS_WITH]-(f:Person) RETURN f.db_id AS id",
|
||||
graphParams(userId),
|
||||
{ database: process.env.GRAPH_DB_NAME },
|
||||
);
|
||||
return new Set(records.map((record) => graphIdToNumber(record.get("id"))));
|
||||
}
|
||||
|
||||
async function getFriendIdsAmong(userId, ids) {
|
||||
if (ids.length === 0) return new Set();
|
||||
const { records } = await driver.executeQuery(
|
||||
`MATCH (me:Person {db_id: $db_id})-[:FRIENDS_WITH]-(f:Person)
|
||||
WHERE f.db_id IN $ids
|
||||
RETURN f.db_id AS id`,
|
||||
{ ...graphParams(userId), ids: ids.map(Number) },
|
||||
{ database: process.env.GRAPH_DB_NAME },
|
||||
);
|
||||
return new Set(records.map((record) => graphIdToNumber(record.get("id"))));
|
||||
}
|
||||
|
||||
async function areFriends(userId, otherId) {
|
||||
const friendIds = await getFriendIdsAmong(userId, [otherId]);
|
||||
return friendIds.has(Number(otherId));
|
||||
}
|
||||
|
||||
module.exports = { getFriendIds, getFriendIdsAmong, areFriends, graphIdToNumber };
|
||||
Reference in New Issue
Block a user