Files
pig/apps/api/src/routes/growth.ts
T
karti 1318c0b841
CI / verify (push) Successful in 3m51s
Ship growth intelligence and demo polish
2026-08-13 04:44:14 -07:00

44 lines
1.5 KiB
TypeScript

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<ApiEnv>) {
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<ApiEnv> {
const routes = new Hono<ApiEnv>();
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;
}