init f0ckm
This commit is contained in:
167
src/inc/routes/reports.mjs
Normal file
167
src/inc/routes/reports.mjs
Normal file
@@ -0,0 +1,167 @@
|
||||
import db from "../sql.mjs";
|
||||
import lib from "../lib.mjs";
|
||||
import audit from "../audit.mjs";
|
||||
|
||||
export default (router, tpl) => {
|
||||
|
||||
// User: Submit a new report
|
||||
router.post(/^\/api\/v2\/report\/?$/, lib.loggedin, async (req, res) => {
|
||||
try {
|
||||
const { item_id, comment_id, reported_user_id, reason } = req.post;
|
||||
|
||||
if (!reason || reason.trim().length === 0) {
|
||||
return res.json({ success: false, msg: "Reason is required." }, 400);
|
||||
}
|
||||
|
||||
// At least one target must be specified
|
||||
if (!item_id && !comment_id && !reported_user_id) {
|
||||
return res.json({ success: false, msg: "Must specify an item, comment, or user to report." }, 400);
|
||||
}
|
||||
|
||||
const reportRes = await db`
|
||||
INSERT INTO reports (reporter_id, item_id, comment_id, user_id, reason)
|
||||
VALUES (
|
||||
${req.session.id},
|
||||
${item_id ? +item_id : null},
|
||||
${comment_id ? +comment_id : null},
|
||||
${reported_user_id ? +reported_user_id : null},
|
||||
${reason.trim()}
|
||||
)
|
||||
RETURNING id
|
||||
`;
|
||||
|
||||
const report_id = reportRes[0].id;
|
||||
|
||||
// Notify all mods and admins
|
||||
try {
|
||||
const mods = await db`SELECT id FROM "user" WHERE admin = true OR is_moderator = true`;
|
||||
if (mods.length > 0) {
|
||||
let resolved_item_id = item_id ? +item_id : null;
|
||||
|
||||
// If reporting a comment, resolve the item_id it belongs to
|
||||
if (!resolved_item_id && comment_id) {
|
||||
const comm = await db`SELECT item_id FROM comments WHERE id = ${comment_id}`;
|
||||
if (comm.length > 0) resolved_item_id = comm[0].item_id;
|
||||
}
|
||||
|
||||
const notificationsToAdd = mods.map(m => ({
|
||||
user_id: m.id,
|
||||
type: 'report',
|
||||
reference_id: report_id,
|
||||
item_id: resolved_item_id // Can be null now after migration
|
||||
}));
|
||||
await db`INSERT INTO notifications ${db(notificationsToAdd)}`;
|
||||
}
|
||||
} catch (notifyErr) {
|
||||
console.error('[REPORT] Failed to send notifications:', notifyErr);
|
||||
}
|
||||
|
||||
return res.json({ success: true, msg: "Report submitted successfully." });
|
||||
} catch (err) {
|
||||
return res.json({ success: false, msg: lib.logError(err) }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Mod/Admin: Get pending/all reports
|
||||
router.get(/^\/api\/v2\/mod\/reports\/?$/, lib.modAuth, async (req, res) => {
|
||||
try {
|
||||
const status = req.url.qs?.status || 'pending';
|
||||
const page = +(req.url.qs?.page || 1);
|
||||
const limit = 20;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const reports = await db`
|
||||
SELECT r.*,
|
||||
rep.user AS reporter_name,
|
||||
COALESCE(tgt_u.user, tgt_auth.user, comm_auth.user) AS reported_user_name,
|
||||
COALESCE(NULLIF(r.user_id, 0), tgt_auth.id, comm_auth.id) AS reported_user_id,
|
||||
COALESCE(tgt_u.admin, tgt_auth.admin, comm_auth.admin) AS reported_user_is_admin,
|
||||
i.dest AS item_dest,
|
||||
tgt_auth.id AS item_user_id,
|
||||
tgt_auth.user AS item_user_name,
|
||||
(SELECT coalesce(json_agg(json_build_object('id', t.id, 'tag', t.tag, 'normalized', t.normalized)), '[]')
|
||||
FROM tags_assign ta
|
||||
JOIN tags t ON ta.tag_id = t.id
|
||||
WHERE ta.item_id = COALESCE(r.item_id, c.item_id)) as item_tags,
|
||||
c.content AS comment_body,
|
||||
COALESCE(r.item_id, c.item_id) AS resolved_item_id,
|
||||
COALESCE(i.dest, ci.dest) AS resolved_item_dest,
|
||||
COALESCE(i.mime, ci.mime) AS resolved_item_mime
|
||||
FROM reports r
|
||||
LEFT JOIN "user" rep ON r.reporter_id = rep.id
|
||||
LEFT JOIN "user" tgt_u ON r.user_id = tgt_u.id
|
||||
LEFT JOIN items i ON r.item_id = i.id
|
||||
LEFT JOIN "user" tgt_auth ON i.username = tgt_auth.user
|
||||
LEFT JOIN comments c ON r.comment_id = c.id
|
||||
LEFT JOIN items ci ON c.item_id = ci.id
|
||||
LEFT JOIN "user" comm_auth ON c.user_id = comm_auth.id
|
||||
WHERE r.status = ${status}
|
||||
ORDER BY r.created_at DESC
|
||||
LIMIT ${limit} OFFSET ${offset}
|
||||
`;
|
||||
|
||||
// Compute badges for tags (sync with lib.getTags logic)
|
||||
for (const r of reports) {
|
||||
if (r.item_tags) {
|
||||
for (const t of r.item_tags) {
|
||||
if (t.tag.startsWith(">")) t.badge = "badge-greentext badge-light";
|
||||
else if (t.normalized === "ukraine") t.badge = "badge-ukraine badge-light";
|
||||
else if (/[а-яё]/.test(t.normalized) || t.normalized === "russia") t.badge = "badge-russia badge-light";
|
||||
else if (t.normalized === "german") t.badge = "badge-german badge-light";
|
||||
else if (t.normalized === "dutch") t.badge = "badge-dutch badge-light";
|
||||
else if (t.normalized === "sfw") t.badge = "badge-success";
|
||||
else if (t.normalized === "nsfw") t.badge = "badge-danger";
|
||||
else t.badge = "badge-light";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const totalRes = await db`SELECT count(*) as c FROM reports WHERE status = ${status}`;
|
||||
const total = totalRes[0].c;
|
||||
|
||||
const emojis = await db`SELECT name, url FROM custom_emojis`;
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
reports,
|
||||
emojis,
|
||||
page,
|
||||
pages: Math.ceil(total / limit),
|
||||
total
|
||||
});
|
||||
} catch (err) {
|
||||
return res.json({ success: false, msg: lib.logError(err) }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Mod/Admin: Resolve a report
|
||||
router.post(/^\/api\/v2\/mod\/reports\/(?<id>\d+)\/resolve\/?$/, lib.modAuth, async (req, res) => {
|
||||
try {
|
||||
const id = +req.params.id;
|
||||
const { action } = req.post; // 'resolved' or 'rejected'
|
||||
|
||||
if (!['resolved', 'rejected'].includes(action)) {
|
||||
return res.json({ success: false, msg: "Invalid action. Must be 'resolved' or 'rejected'." }, 400);
|
||||
}
|
||||
|
||||
const result = await db`
|
||||
UPDATE reports
|
||||
SET status = ${action}, resolved_by = ${req.session.id}
|
||||
WHERE id = ${id}
|
||||
RETURNING id
|
||||
`;
|
||||
|
||||
if (result.length === 0) {
|
||||
return res.json({ success: false, msg: "Report not found." }, 404);
|
||||
}
|
||||
|
||||
await audit.log(req.session.id, 'resolve_report', 'report', id, { status: action });
|
||||
|
||||
return res.json({ success: true, msg: `Report marked as ${action}.` });
|
||||
} catch (err) {
|
||||
return res.json({ success: false, msg: lib.logError(err) }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
Reference in New Issue
Block a user