293 lines
12 KiB
JavaScript
293 lines
12 KiB
JavaScript
import db from "../sql.mjs";
|
|
import f0cklib from "../routeinc/f0cklib.mjs"; // Assuming this exists or we need to check imports
|
|
|
|
export default (router, tpl) => {
|
|
|
|
|
|
|
|
// Get comments for an item
|
|
router.get(/\/api\/comments\/(?<itemid>\d+)/, async (req, res) => {
|
|
const itemId = req.params.itemid;
|
|
const sort = req.url.qs?.sort || 'new'; // 'new' or 'old'
|
|
|
|
// Require login
|
|
if (!req.session) {
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
success: true,
|
|
comments: [],
|
|
require_login: true,
|
|
user_id: null,
|
|
is_admin: false
|
|
})
|
|
});
|
|
}
|
|
|
|
try {
|
|
// Check locked status
|
|
const item = await db`SELECT is_comments_locked FROM items WHERE id = ${itemId}`;
|
|
const is_locked = item.length > 0 ? item[0].is_comments_locked : false;
|
|
|
|
const comments = await f0cklib.getComments(itemId, sort);
|
|
|
|
let is_subscribed = false;
|
|
if (req.session) {
|
|
const sub = await db`SELECT 1 FROM comment_subscriptions WHERE user_id = ${req.session.id} AND item_id = ${itemId}`;
|
|
if (sub.length > 0) is_subscribed = true;
|
|
}
|
|
|
|
// Transform for frontend if needed, or send as is
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
success: true,
|
|
comments,
|
|
is_subscribed,
|
|
is_locked,
|
|
user_id: req.session ? req.session.user : null,
|
|
is_admin: req.session ? req.session.admin : false
|
|
})
|
|
})
|
|
} catch (err) {
|
|
console.error(err);
|
|
return res.reply({
|
|
code: 500,
|
|
body: JSON.stringify({ success: false, message: "Database error" })
|
|
});
|
|
}
|
|
});
|
|
|
|
// Post a comment
|
|
router.post('/api/comments', async (req, res) => {
|
|
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false, message: "Unauthorized" }) });
|
|
|
|
console.log("DEBUG: POST /api/comments");
|
|
|
|
// Use standard framework parsing
|
|
const body = req.post || {};
|
|
const item_id = parseInt(body.item_id, 10);
|
|
const parent_id = body.parent_id ? parseInt(body.parent_id, 10) : null;
|
|
const content = body.content;
|
|
|
|
console.log("DEBUG: Posting comment:", { item_id, parent_id, content: content?.substring(0, 20) });
|
|
|
|
if (!content || !content.trim()) {
|
|
return res.reply({ body: JSON.stringify({ success: false, message: "Empty comment" }) });
|
|
}
|
|
|
|
try {
|
|
// Check if thread is locked (admins can still post)
|
|
if (!req.session.admin) {
|
|
const lockCheck = await db`SELECT COALESCE(is_comments_locked, false) as is_locked FROM items WHERE id = ${item_id}`;
|
|
if (lockCheck.length > 0 && lockCheck[0].is_locked) {
|
|
return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "This thread is locked" }) });
|
|
}
|
|
}
|
|
|
|
const newComment = await db`
|
|
INSERT INTO comments ${db({
|
|
item_id,
|
|
user_id: req.session.id,
|
|
parent_id: parent_id || null,
|
|
content: content
|
|
})}
|
|
RETURNING id, created_at
|
|
`;
|
|
|
|
const commentId = parseInt(newComment[0].id, 10);
|
|
|
|
// Notify Subscribers (excluding the author)
|
|
// 1. Get subscribers of the item
|
|
// 2. If it's a reply, notify parent author? (Optional, complex logic. Let's stick to item subscription for now + Parent author)
|
|
|
|
// Logic: Notify users who subscribed to this item OR are the parent author.
|
|
// Exclude current user.
|
|
|
|
// 1. Get subscribers
|
|
const subscribers = await db`SELECT user_id FROM comment_subscriptions WHERE item_id = ${item_id}`;
|
|
|
|
// 2. Get parent author
|
|
let parentAuthor = [];
|
|
if (parent_id) {
|
|
parentAuthor = await db`SELECT user_id FROM comments WHERE id = ${parent_id}`;
|
|
}
|
|
|
|
// 3. Prepare notifications
|
|
const notificationsToAdd = [];
|
|
|
|
// Parent author gets 'comment_reply'
|
|
if (parentAuthor.length > 0) {
|
|
const pid = parentAuthor[0].user_id;
|
|
if (pid !== req.session.id) {
|
|
notificationsToAdd.push({
|
|
user_id: pid,
|
|
type: 'comment_reply',
|
|
item_id: item_id,
|
|
reference_id: commentId
|
|
});
|
|
}
|
|
}
|
|
|
|
// Subscribers get 'subscription' (unless they already got comment_reply)
|
|
const parentUserId = parentAuthor.length > 0 ? parentAuthor[0].user_id : -1;
|
|
|
|
subscribers.forEach(s => {
|
|
if (s.user_id !== req.session.id && s.user_id !== parentUserId) {
|
|
notificationsToAdd.push({
|
|
user_id: s.user_id,
|
|
type: 'subscription',
|
|
item_id: item_id,
|
|
reference_id: commentId
|
|
});
|
|
}
|
|
});
|
|
|
|
// 4. Batch insert
|
|
if (notificationsToAdd.length > 0) {
|
|
await db`INSERT INTO notifications ${db(notificationsToAdd)}`;
|
|
}
|
|
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ success: true, comment: newComment[0] })
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
return res.reply({
|
|
code: 500,
|
|
body: JSON.stringify({ success: false, message: "Database error" })
|
|
});
|
|
}
|
|
});
|
|
|
|
// Subscribe toggle
|
|
router.post(/\/api\/subscribe\/(?<itemid>\d+)/, async (req, res) => {
|
|
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
|
const itemId = req.params.itemid;
|
|
|
|
try {
|
|
const existing = await db`
|
|
SELECT 1 FROM comment_subscriptions
|
|
WHERE user_id = ${req.session.id} AND item_id = ${itemId}
|
|
`;
|
|
|
|
let subscribed = false;
|
|
if (existing.length > 0) {
|
|
await db`DELETE FROM comment_subscriptions WHERE user_id = ${req.session.id} AND item_id = ${itemId}`;
|
|
} else {
|
|
await db`INSERT INTO comment_subscriptions (user_id, item_id) VALUES (${req.session.id}, ${itemId})`;
|
|
subscribed = true;
|
|
}
|
|
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ success: true, subscribed })
|
|
});
|
|
} catch (e) {
|
|
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
|
}
|
|
});
|
|
|
|
// Delete comment
|
|
router.post(/\/api\/comments\/(?<id>\d+)\/delete/, async (req, res) => {
|
|
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
|
const commentId = req.params.id;
|
|
|
|
try {
|
|
const comment = await db`SELECT user_id FROM comments WHERE id = ${commentId}`;
|
|
if (!comment.length) return res.reply({ code: 404, body: JSON.stringify({ success: false, message: "Not found" }) });
|
|
|
|
if (!req.session.admin && comment[0].user_id !== req.session.id) {
|
|
return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "Forbidden" }) });
|
|
}
|
|
|
|
await db`UPDATE comments SET is_deleted = true, content = '[deleted]' WHERE id = ${commentId}`;
|
|
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ success: true })
|
|
});
|
|
} catch (e) {
|
|
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
|
}
|
|
});
|
|
|
|
// Edit comment (admin only)
|
|
router.post(/\/api\/comments\/(?<id>\d+)\/edit/, async (req, res) => {
|
|
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
|
if (!req.session.admin) return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "Admin only" }) });
|
|
|
|
const commentId = req.params.id;
|
|
const body = req.post || {};
|
|
const content = body.content;
|
|
|
|
if (!content || !content.trim()) {
|
|
return res.reply({ body: JSON.stringify({ success: false, message: "Empty content" }) });
|
|
}
|
|
|
|
try {
|
|
const comment = await db`SELECT id FROM comments WHERE id = ${commentId}`;
|
|
if (!comment.length) return res.reply({ code: 404, body: JSON.stringify({ success: false, message: "Not found" }) });
|
|
|
|
await db`UPDATE comments SET content = ${content}, updated_at = NOW() WHERE id = ${commentId}`;
|
|
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ success: true })
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
|
}
|
|
});
|
|
// Toggle pin comment (admin only)
|
|
router.post(/\/api\/comments\/(?<id>\d+)\/pin/, async (req, res) => {
|
|
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
|
if (!req.session.admin) return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "Admin only" }) });
|
|
|
|
const commentId = req.params.id;
|
|
|
|
try {
|
|
const comment = await db`SELECT id, COALESCE(is_pinned, false) as is_pinned FROM comments WHERE id = ${commentId}`;
|
|
if (!comment.length) return res.reply({ code: 404, body: JSON.stringify({ success: false, message: "Not found" }) });
|
|
|
|
const newPinned = !comment[0].is_pinned;
|
|
await db`UPDATE comments SET is_pinned = ${newPinned} WHERE id = ${commentId}`;
|
|
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ success: true, is_pinned: newPinned })
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
|
}
|
|
});
|
|
// Toggle lock thread (admin only)
|
|
router.post(/\/api\/comments\/(?<itemid>\d+)\/lock/, async (req, res) => {
|
|
if (!req.session) return res.reply({ code: 401, body: JSON.stringify({ success: false }) });
|
|
if (!req.session.admin) return res.reply({ code: 403, body: JSON.stringify({ success: false, message: "Admin only" }) });
|
|
|
|
const itemId = req.params.itemid;
|
|
|
|
try {
|
|
const item = await db`SELECT id, COALESCE(is_comments_locked, false) as is_locked FROM items WHERE id = ${itemId}`;
|
|
if (!item.length) return res.reply({ code: 404, body: JSON.stringify({ success: false, message: "Not found" }) });
|
|
|
|
const newLocked = !item[0].is_locked;
|
|
await db`UPDATE items SET is_comments_locked = ${newLocked} WHERE id = ${itemId}`;
|
|
|
|
return res.reply({
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ success: true, is_locked: newLocked })
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
return res.reply({ code: 500, body: JSON.stringify({ success: false }) });
|
|
}
|
|
});
|
|
|
|
return router;
|
|
};
|