import { NextResponse } from 'next/server'; import { z } from 'zod'; import { withAuth, withPermission } from '@/lib/api/helpers'; import { parseBody } from '@/lib/api/route-helpers'; import { errorResponse, ForbiddenError } from '@/lib/errors'; import { createCrmInvite, listCrmInvites } from '@/lib/services/crm-invite.service'; export const GET = withAuth( withPermission('admin', 'manage_users', async (_req, ctx) => { try { // crm_user_invites is a global table (no per-port column) — invites // mint better-auth users that may later be assigned roles in any // port. Listing it cross-tenant would let a port-A director // enumerate pending invitee emails, names, and isSuperAdmin flags // for every other tenant. Restrict the listing to super-admins. if (!ctx.isSuperAdmin) { throw new ForbiddenError('Listing CRM invites requires super-admin'); } const data = await listCrmInvites(); return NextResponse.json({ data }); } catch (error) { return errorResponse(error); } }), ); const createInviteSchema = z.object({ email: z.string().email(), name: z.string().min(1).max(200).optional(), isSuperAdmin: z.boolean().optional().default(false), }); export const POST = withAuth( withPermission('admin', 'manage_users', async (req, ctx) => { try { const body = await parseBody(req, createInviteSchema); // Only existing super-admins can mint super-admin invitations. The // manage_users permission is granted to port-scoped director roles, // which must not be able to elevate themselves cross-tenant by // inviting a fresh super_admin. if (body.isSuperAdmin && !ctx.isSuperAdmin) { throw new ForbiddenError('Only super admins can mint super-admin invitations'); } const result = await createCrmInvite({ ...body, invitedBy: ctx }); return NextResponse.json({ data: result }, { status: 201 }); } catch (error) { return errorResponse(error); } }), );