Barazo AppView backend barazo.forum
at main 91 lines 3.4 kB view raw
1import { z } from 'zod/v4' 2import type { FastifyPluginCallback } from 'fastify' 3import { sendError } from '../lib/api-errors.js' 4 5// --------------------------------------------------------------------------- 6// Zod schemas for request validation 7// --------------------------------------------------------------------------- 8 9const initializeBodySchema = z.object({ 10 communityName: z.string().trim().min(1).max(255).optional(), 11 handle: z.string().trim().min(1).max(253).optional(), 12 serviceEndpoint: z.url().optional(), 13}) 14 15// --------------------------------------------------------------------------- 16// Setup routes plugin 17// --------------------------------------------------------------------------- 18 19/** 20 * Setup wizard routes for first-time community initialization. 21 * 22 * - GET /api/setup/status -- Check if community is initialized (public) 23 * - POST /api/setup/initialize -- Initialize community with first admin (auth required) 24 * 25 * When handle and serviceEndpoint are both provided in the initialize request, 26 * a PLC DID is generated and registered with plc.directory. 27 */ 28export function setupRoutes(): FastifyPluginCallback { 29 return (app, _opts, done) => { 30 const { setupService, authMiddleware } = app 31 32 // ------------------------------------------------------------------- 33 // GET /api/setup/status (public, no auth required) 34 // ------------------------------------------------------------------- 35 36 app.get('/api/setup/status', async (request, reply) => { 37 try { 38 const status = await setupService.getStatus(request.communityDid ?? '') 39 return await reply.status(200).send(status) 40 } catch (err: unknown) { 41 app.log.error({ err }, 'Failed to get setup status') 42 return sendError(reply, 500, 'Service temporarily unavailable') 43 } 44 }) 45 46 // ------------------------------------------------------------------- 47 // POST /api/setup/initialize (requires auth) 48 // ------------------------------------------------------------------- 49 50 app.post( 51 '/api/setup/initialize', 52 { preHandler: [authMiddleware.requireAuth] }, 53 async (request, reply) => { 54 // Validate request body 55 const parsed = initializeBodySchema.safeParse(request.body) 56 if (!parsed.success) { 57 return await reply.status(400).send({ error: 'Invalid request body' }) 58 } 59 60 // request.user is guaranteed by requireAuth 61 const user = request.user 62 if (!user) { 63 return await reply.status(401).send({ error: 'Authentication required' }) 64 } 65 66 try { 67 const result = await setupService.initialize({ 68 communityDid: request.communityDid ?? '', 69 did: user.did, 70 communityName: parsed.data.communityName, 71 handle: parsed.data.handle, 72 serviceEndpoint: parsed.data.serviceEndpoint, 73 }) 74 75 if ('alreadyInitialized' in result) { 76 return await reply.status(409).send({ 77 error: 'Community already initialized', 78 }) 79 } 80 81 return await reply.status(200).send(result) 82 } catch (err: unknown) { 83 app.log.error({ err }, 'Failed to initialize community') 84 return sendError(reply, 500, 'Service temporarily unavailable') 85 } 86 } 87 ) 88 89 done() 90 } 91}