Added liking and security measures

This commit is contained in:
Sven laptop
2026-07-31 15:11:57 +02:00
parent 494b583cbc
commit ab73eecfec
25 changed files with 1095 additions and 91 deletions
+72 -9
View File
@@ -18,6 +18,9 @@ const {
removeUploadedProfileFile,
uploadDirectory,
} = require("../../middleware/post_upload");
const { areFriends } = require("../relations/friend_graph");
const { authLimiter } = require("../../middleware/rate_limit");
const { deletePostsByAuthor } = require("../posts/post_graph");
const scrypt = promisify(crypto.scrypt);
@@ -49,16 +52,43 @@ function resolveProfileId(req) {
return id;
}
function isPositiveInteger(value) {
const id = Number(value);
return Number.isInteger(id) && id > 0;
}
function limitedProfile(profile) {
return {
id: profile.id,
name: profile.name,
username: profile.username,
};
}
async function getProfile(req, res) {
const id = resolveProfileId(req);
if (id !== req.user.id && !isPositiveInteger(id)) {
return res.status(400).send("Invalid profile id");
}
try {
const { rows } = await pool.query(
"SELECT id, name, username, profile_link FROM people WHERE id = $1",
"SELECT id, name, username, profile_link, private FROM people WHERE id = $1",
[id],
);
if (rows.length === 0) return res.status(404).send("Profile not found");
return res.status(200).json(rows[0]);
const profile = rows[0];
const targetId = Number(id);
if (
profile.private &&
targetId !== req.user.id &&
!(await areFriends(req.user.id, targetId))
) {
return res.status(200).json(limitedProfile(profile));
}
return res.status(200).json(profile);
} catch (err) {
console.error("profile query failed", {
message: err.message,
@@ -132,6 +162,7 @@ async function updateProfile(req, res) {
if (update.name !== undefined) addField("name", update.name);
if (update.username !== undefined) addField("username", update.username);
if (update.email !== undefined) addField("email", update.email);
if (update.private !== undefined) addField("private", update.private);
if (update.password !== undefined) {
addField("password", await hashPassword(update.password));
}
@@ -142,7 +173,7 @@ async function updateProfile(req, res) {
let rows;
try {
({ rows } = await pool.query(
`UPDATE people SET ${fields.join(", ")} WHERE id = $${values.length} RETURNING id, name, username, profile_link`,
`UPDATE people SET ${fields.join(", ")} WHERE id = $${values.length} RETURNING id, name, username, profile_link, private`,
values,
));
} catch (err) {
@@ -156,6 +187,11 @@ async function updateProfile(req, res) {
});
}
if (update.password !== undefined) {
await pool.query("UPDATE people SET token_version = token_version + 1 WHERE id = $1", [req.user.id]);
await pool.query("DELETE FROM sessions WHERE user_id = $1", [req.user.id]);
}
if (update.name !== undefined || update.username !== undefined) {
await driver.executeQuery(
"MATCH (p:Person {db_id: $db_id}) SET p.name = $name, p.username = $username",
@@ -207,9 +243,11 @@ async function deleteAccount(req, res) {
}
await removeImageLink(existing.rows[0].profile_link);
await pool.query("DELETE FROM posts WHERE author_id = $1", [req.user.id]);
await pool.query("DELETE FROM sessions WHERE user_id = $1", [req.user.id]);
await pool.query("UPDATE posts SET active = false WHERE author_id = $1", [req.user.id]);
await pool.query("DELETE FROM people WHERE id = $1", [req.user.id]);
await deletePostsByAuthor(req.user.id);
await driver.executeQuery(
"MATCH (p:Person {db_id: $db_id}) DETACH DELETE p",
{ db_id: req.user.id },
@@ -237,12 +275,36 @@ async function deleteAccount(req, res) {
router.get(
"/image/:filename",
strictInput(),
(req, res) => {
async (req, res) => {
const filename = path.basename(req.params.filename);
if (filename !== req.params.filename) return res.status(404).end();
res.sendFile(filename, { root: uploadDirectory }, (err) => {
if (err && !res.headersSent) res.status(err.statusCode === 404 ? 404 : 500).end();
});
try {
const { rows } = await pool.query(
"SELECT id, private FROM people WHERE profile_link = $1",
[`/profiles/image/${filename}`],
);
if (rows.length === 0) return res.status(404).send("Image not found");
const profile = rows[0];
if (
profile.private &&
Number(profile.id) !== req.user.id &&
!(await areFriends(req.user.id, profile.id))
) {
return res.status(404).send("Image not found");
}
res.set("Cache-Control", "private, no-store");
res.sendFile(filename, { root: uploadDirectory }, (err) => {
if (err && !res.headersSent) res.status(err.statusCode === 404 ? 404 : 500).end();
});
} catch (err) {
console.error("profile image query failed", {
message: err.message,
code: err.code,
detail: err.detail,
hint: err.hint,
});
return res.status(500).send("Image could not be retrieved, request failed");
}
},
);
@@ -252,7 +314,7 @@ router.put(
"/me",
handleProfileUpload,
strictInput({
body: ["name", "username", "email", "password", "current_password", "remove_image"],
body: ["name", "username", "email", "password", "current_password", "remove_image", "private"],
cleanupUploadedFile: true,
}),
sanitizeProfileUpdate,
@@ -260,6 +322,7 @@ router.put(
);
router.delete(
"/me",
authLimiter,
strictInput({ body: ["password"] }),
sanitizeAccountDelete,
deleteAccount,