source: Klonkt/src/routes/admin-comments.js@ a4f6827

main
Last change on this file since a4f6827 was 8cb1dc7, checked in by roboburr <roboburr@…>, 3 months ago

hub: artist self-management — owner manages their own subsite (posts/appearance/comments)

A site owner (member) now gets a "My PrutFolio" dashboard and can manage their
OWN site; cross-site access is restricted.

  • auth.js: requireSiteManager (owner of res.locals.site or god) + requireSiteManagerBySlug (owner of :slug or god).
  • admin.js: /admin role-aware -> god=hub management, owner=my-site dashboard, member without site=403.
  • admin-sites.js: /:slug/edit + /save owner-or-god (was god-only); upload-photo requireAuth. list/new/create/delete remain god-only.
  • admin-comments.js: requireSiteManager + redirects/form actions via siteUrlBase so the artist context (/user/<slug>/admin/comments) is correct.
  • render.js: userOwnsSite flag; topnav + overview show "Admin" for owners; topnav "New post" uses siteUrlBase.
  • pages/my-site.ejs: artist dashboard.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 2.8 KB
Line 
1/**
2 * Admin: Comment moderation queue — Phase E.
3 *
4 * GET /admin/comments -> list pending + recent (god-only)
5 * POST /admin/comments/:id/approve -> set status = 'approved'
6 * POST /admin/comments/:id/reject -> set status = 'rejected' (keeps the row
7 * so we have a paper trail; admin can
8 * hard-delete via the post page).
9 *
10 * Scope: shows comments for the resolved site only (the one matched by
11 * /sites/:slug or default). Future: filter by status / search.
12 */
13
14import express from 'express';
15import db from '../config/database.js';
16import { renderPage } from '../middleware/render.js';
17import { requireSiteManager } from '../middleware/auth.js';
18
19const router = express.Router();
20
21router.get('/', requireSiteManager, (req, res) => {
22 const site = res.locals.site;
23 if (!site) return res.status(404).send('No site');
24
25 const pending = db.prepare(`
26 SELECT c.id, c.content, c.created_at, c.parent_comment_id,
27 u.username AS author_username,
28 p.slug AS post_slug, p.title AS post_title
29 FROM comments c
30 JOIN users u ON u.id = c.author_id
31 JOIN posts p ON p.id = c.post_id
32 WHERE p.site_id = ? AND c.status = 'pending'
33 ORDER BY c.created_at ASC
34 LIMIT 200
35 `).all(site.id);
36
37 const recent = db.prepare(`
38 SELECT c.id, c.content, c.created_at, c.status,
39 u.username AS author_username,
40 p.slug AS post_slug, p.title AS post_title
41 FROM comments c
42 JOIN users u ON u.id = c.author_id
43 JOIN posts p ON p.id = c.post_id
44 WHERE p.site_id = ? AND c.status IN ('approved', 'rejected')
45 ORDER BY c.created_at DESC
46 LIMIT 30
47 `).all(site.id);
48
49 renderPage(req, res, 'pages/admin-comments', {
50 pageTitle: 'Comment moderation',
51 bodyClass: 'on-admin',
52 pending,
53 recent,
54 moderationMode: site.comments_moderation_mode || 'trust',
55 success: req.query.success || null,
56 error: req.query.error || null,
57 });
58});
59
60function setStatus(req, res, status) {
61 const site = res.locals.site;
62 if (!site) return res.status(404).send('No site');
63 const base = res.locals.siteUrlBase || ''; // /user/<slug> in hub-artiestcontext, anders ''
64
65 const row = db.prepare(`
66 SELECT c.id FROM comments c JOIN posts p ON p.id = c.post_id
67 WHERE c.id = ? AND p.site_id = ?
68 `).get(req.params.id, site.id);
69
70 if (!row) return res.redirect(base + '/admin/comments?error=Not+found');
71
72 db.prepare(
73 'UPDATE comments SET status = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?'
74 ).run(status, req.params.id);
75 res.redirect(base + '/admin/comments?success=' + encodeURIComponent('Comment ' + status));
76}
77
78router.post('/:id/approve', requireSiteManager, (req, res) => setStatus(req, res, 'approved'));
79router.post('/:id/reject', requireSiteManager, (req, res) => setStatus(req, res, 'rejected'));
80
81export default router;
Note: See TracBrowser for help on using the repository browser.