Barazo AppView backend
barazo.forum
1import type { FastifyReply, FastifyRequest } from 'fastify'
2import { eq } from 'drizzle-orm'
3import type { AuthMiddleware } from './middleware.js'
4import type { Database } from '../db/index.js'
5import type { Logger } from '../lib/logger.js'
6import { users } from '../db/schema/users.js'
7
8/**
9 * Create a requireModerator preHandler hook for Fastify routes.
10 *
11 * This middleware:
12 * 1. Delegates to requireAuth to verify the user is authenticated
13 * 2. Looks up the user in the database by DID
14 * 3. Checks if the user has the "moderator" or "admin" role
15 * 4. Returns 403 if the user does not have sufficient privileges
16 * 5. Logs moderator access attempts for audit trail
17 *
18 * @param db - Database instance for user lookups
19 * @param authMiddleware - Auth middleware with requireAuth hook
20 * @param logger - Optional Pino logger for audit trail
21 * @returns A Fastify preHandler function
22 */
23export function createRequireModerator(
24 db: Database,
25 authMiddleware: AuthMiddleware,
26 logger?: Logger
27): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
28 return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
29 // First, run requireAuth to verify authentication
30 await authMiddleware.requireAuth(request, reply)
31
32 // If requireAuth sent a response (e.g. 401), stop here
33 if (reply.sent) {
34 return
35 }
36
37 // At this point request.user should be set by requireAuth
38 if (!request.user) {
39 logger?.warn(
40 { url: request.url, method: request.method },
41 'Moderator access denied: no user after auth'
42 )
43 await reply.status(403).send({ error: 'Moderator access required' })
44 return
45 }
46
47 // Look up user role in database
48 const rows = await db.select().from(users).where(eq(users.did, request.user.did))
49
50 const userRow = rows[0]
51 if (!userRow || (userRow.role !== 'moderator' && userRow.role !== 'admin')) {
52 logger?.warn(
53 { did: request.user.did, role: userRow?.role, url: request.url, method: request.method },
54 'Moderator access denied: insufficient role'
55 )
56 await reply.status(403).send({ error: 'Moderator access required' })
57 return
58 }
59
60 logger?.info(
61 { did: request.user.did, role: userRow.role, url: request.url, method: request.method },
62 'Moderator access granted'
63 )
64 }
65}