Barazo AppView backend
barazo.forum
1import type { FastifyReply, FastifyRequest } from 'fastify'
2import type { AuthMiddleware } from './middleware.js'
3import type { Env } from '../config/env.js'
4import type { Logger } from '../lib/logger.js'
5
6/**
7 * Create a requireOperator preHandler hook for Fastify routes.
8 *
9 * This middleware:
10 * 1. Returns 404 if COMMUNITY_MODE is not "multi" (hides operator routes in single mode)
11 * 2. Delegates to requireAuth to verify the user is authenticated
12 * 3. Checks if the user's DID is in the OPERATOR_DIDS list
13 * 4. Returns 403 if the user is not an operator
14 *
15 * Operators may not exist in the users table -- they are platform-level admins
16 * identified solely by their DID in the environment config.
17 */
18export function createRequireOperator(
19 env: Env,
20 authMiddleware: AuthMiddleware,
21 logger?: Logger
22): (request: FastifyRequest, reply: FastifyReply) => Promise<void> {
23 return async (request: FastifyRequest, reply: FastifyReply): Promise<void> => {
24 // Multi-mode-only routes return 404 in single-community mode
25 if (env.COMMUNITY_MODE !== 'multi') {
26 await reply.status(404).send({ error: 'Not found' })
27 return
28 }
29
30 // Verify authentication
31 await authMiddleware.requireAuth(request, reply)
32
33 if (reply.sent) {
34 return
35 }
36
37 if (!request.user) {
38 logger?.warn(
39 { url: request.url, method: request.method },
40 'Operator access denied: no user after auth'
41 )
42 await reply.status(403).send({ error: 'Operator access required' })
43 return
44 }
45
46 // Check if DID is in the operator list
47 if (!env.OPERATOR_DIDS.includes(request.user.did)) {
48 logger?.warn(
49 { did: request.user.did, url: request.url, method: request.method },
50 'Operator access denied: DID not in OPERATOR_DIDS'
51 )
52 await reply.status(403).send({ error: 'Operator access required' })
53 return
54 }
55
56 logger?.info(
57 { did: request.user.did, url: request.url, method: request.method },
58 'Operator access granted'
59 )
60 }
61}