import type { Database } from '@pig/db'; import { Hono, type Context } from 'hono'; import { z } from 'zod'; import type { ApiEnv } from '../lib/mutation'; import { apiError } from '../lib/mutation'; import { CustomerLifecycleService } from '../services/customer-lifecycle'; const accountIdSchema = z.string().uuid(); export function growthReadAllowed(scopes: readonly string[]): boolean { return scopes.includes('read'); } function requireGrowthRead(context: Context) { if (growthReadAllowed(context.get('principal').scopes)) return null; return context.json( apiError('insufficient_scope', "This credential lacks the 'read' scope."), 403, ); } export function createGrowthRoutes(db: Database): Hono { const routes = new Hono(); const service = new CustomerLifecycleService(db); routes.get('/api/growth', async (context) => { const denied = requireGrowthRead(context); return denied ?? context.json(await service.report()); }); routes.get('/api/growth/accounts/:id', async (context) => { const denied = requireGrowthRead(context); if (denied) return denied; const accountId = accountIdSchema.safeParse(context.req.param('id')); if (!accountId.success) { return context.json(apiError('invalid_account', 'Invalid account ID.', accountId.error.issues), 400); } const customer = await service.account(accountId.data); return customer ? context.json(customer) : context.json(apiError('not_found', 'Growth account not found.'), 404); }); return routes; }