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\/(?\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\/(?\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\/(?\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\/(?\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\/(?\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\/(?\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; };