/** * Users Routes – Admin user management */ import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; import type { DatabaseInterface, DBUser } from '../database/DatabaseInterface.js'; import type { AuthService } from '../auth/AuthService.js'; function extractToken(request: FastifyRequest): string | null { const auth = request.headers?.authorization; if (auth && auth.startsWith('Bearer ')) { return auth.slice(7); } return null; } function requireAdmin(request: FastifyRequest, reply: FastifyReply, authService: AuthService): DBUser | null { const token = extractToken(request); if (!token) { reply.code(401).send({ error: 'Authentication required' }); return null; } const user = authService.getUserFromSession(token); if (!user) { reply.code(401).send({ error: 'Invalid or expired session' }); return null; } if (user.role !== 'admin') { reply.code(403).send({ error: 'Admin access required' }); return null; } return user; } export function registerUserRoutes(fastify: FastifyInstance, db: DatabaseInterface, authService: AuthService) { // List all users (admin only) fastify.get('/api/users', async (request, reply) => { if (!requireAdmin(request, reply, authService)) return; const users = db.listUsers(); return users.map(({ password_hash, ...safe }) => safe); }); // Get single user (admin only) fastify.get('/api/users/:id', async (request, reply) => { if (!requireAdmin(request, reply, authService)) return; const { id } = request.params as { id: string }; const user = db.getUser(id); if (!user) return reply.code(404).send({ error: 'User not found' }); const { password_hash, ...safe } = user; return safe; }); // Update user (admin only) fastify.patch('/api/users/:id', async (request, reply) => { if (!requireAdmin(request, reply, authService)) return; const { id } = request.params as { id: string }; const body = request.body as Partial; // Prevent password update via this endpoint delete body.password_hash; const updated = db.updateUser(id, body); if (!updated) return reply.code(404).send({ error: 'User not found' }); const { password_hash, ...safe } = updated; return safe; }); // Delete user (admin only) fastify.delete('/api/users/:id', async (request, reply) => { if (!requireAdmin(request, reply, authService)) return; const { id } = request.params as { id: string }; const ok = db.deleteUser(id); if (!ok) return reply.code(404).send({ error: 'User not found' }); return reply.code(204).send(); }); }