Merge branch 'main' into fix/stale-jwt-revalidation

This commit is contained in:
2026-07-03 02:31:59 +00:00
2 changed files with 21 additions and 4 deletions

View File

@@ -5,7 +5,7 @@ const multer = require('multer')
const { body, param } = require('express-validator') const { body, param } = require('express-validator')
const ctrl = require('./admin.controller') const ctrl = require('./admin.controller')
const { isLoggedIn } = require('../../../utils/auth') const { isLoggedIn, requireRole } = require('../../../utils/auth')
const noindex = require('../../../middleware/noindex') const noindex = require('../../../middleware/noindex')
const validate = require('../../../middleware/validate') const validate = require('../../../middleware/validate')
@@ -14,6 +14,10 @@ const adminRouter = express.Router()
// Every admin route requires auth and is kept out of search indexes. // Every admin route requires auth and is kept out of search indexes.
adminRouter.use(noindex, isLoggedIn) adminRouter.use(noindex, isLoggedIn)
// Admin-only gate. Editors may manage content (posts/wiki), but user
// management, site mode, and settings are restricted to the admin role.
const adminOnly = requireRole('admin')
// ── Image uploads (screenshots/gallery) ─────────────────────────────── // ── Image uploads (screenshots/gallery) ───────────────────────────────
const UPLOAD_DIR = const UPLOAD_DIR =
process.env.UPLOAD_DIR || path.join(__dirname, '..', '..', '..', '..', 'uploads') process.env.UPLOAD_DIR || path.join(__dirname, '..', '..', '..', '..', 'uploads')
@@ -39,6 +43,7 @@ const upload = multer({
adminRouter.get('/dashboard', ctrl.dashboard) adminRouter.get('/dashboard', ctrl.dashboard)
adminRouter.put( adminRouter.put(
'/site-mode', '/site-mode',
adminOnly,
body('mode').isIn(['live', 'maintenance']), body('mode').isIn(['live', 'maintenance']),
validate, validate,
ctrl.setSiteMode, ctrl.setSiteMode,
@@ -135,13 +140,14 @@ adminRouter.post(
adminRouter.delete('/wiki/:slug', ctrl.deleteWiki) adminRouter.delete('/wiki/:slug', ctrl.deleteWiki)
// ── Settings ────────────────────────────────────────────────────────── // ── Settings ──────────────────────────────────────────────────────────
adminRouter.get('/settings', ctrl.getSettings) adminRouter.get('/settings', adminOnly, ctrl.getSettings)
adminRouter.put('/settings', ctrl.updateSettings) adminRouter.put('/settings', adminOnly, ctrl.updateSettings)
// ── Activity log ────────────────────────────────────────────────────── // ── Activity log ──────────────────────────────────────────────────────
adminRouter.get('/activity', ctrl.listActivity) adminRouter.get('/activity', ctrl.listActivity)
// ── User management ─────────────────────────────────────────────────── // ── User management (admin only) ──────────────────────────────────────
adminRouter.use('/users', adminOnly)
adminRouter.get('/users', ctrl.listUsers) adminRouter.get('/users', ctrl.listUsers)
adminRouter.post( adminRouter.post(
'/users', '/users',

View File

@@ -96,6 +96,16 @@ async function isLoggedIn(req, res, next) {
} }
} }
// Gate middleware factory: allow only the listed roles. Assumes isLoggedIn ran
// first so req.user is populated. Use for admin-only endpoints (users, site
// mode, settings) so a lower-privilege editor cannot reach them.
function requireRole(...roles) {
return (req, res, next) => {
if (roles.includes(req.user?.role)) return next()
return res.status(403).json({ message: 'Forbidden' })
}
}
module.exports = { module.exports = {
COOKIE_NAME, COOKIE_NAME,
signToken, signToken,
@@ -104,4 +114,5 @@ module.exports = {
clearAuthCookie, clearAuthCookie,
getUserFromRequest, getUserFromRequest,
isLoggedIn, isLoggedIn,
requireRole,
} }