Barazo AppView backend
barazo.forum
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}