/** * Mount point for `/api/org/:orgSlug/*` org-admin APIs. */ import type { PrismaClient } from "@prisma/client"; import type { FastifyInstance } from "fastify"; import { requireOrgRole, type GuardDeps } from "../auth/guards.js"; import { handleRouteError } from "../errors.js"; import { ensureOrganizationProjectSettings, setMembersCanCreateProjects, } from "../../projectOnboarding.js"; import { registerAccessRoutes } from "./accessRoutes.js"; import { registerExplorerRoutes } from "./explorerRoutes.js"; import { registerMembersRoutes } from "./membersRoutes.js"; import { registerSessionsAndUsageRoutes } from "./sessionsRoutes.js"; import { registerTeamsRoutes } from "./teamsRoutes.js"; import { registerProviderConnectionRoutes } from "./providerConnectionRoutes.js"; import { registerCapacityRoutes } from "./capacityRoutes.js"; import { readSkillStoreRoot } from "../../agent/skillStore.js"; import { registerAgentConfigRoutes } from "./agentConfigRoutes.js"; import type { LocalSecretEnvelope } from "../../security/secretEnvelope.js"; import type { ProviderReadinessProbe } from "../../connections/providerReadiness.js"; import type { FeishuReadinessProbe } from "../../connections/feishuReadiness.js"; import { registerFeishuApplicationConnectionRoutes } from "./feishuApplicationConnectionRoutes.js"; export interface OrgRouteConfig { readonly prisma: PrismaClient; readonly sessionSecret: string; readonly projectWorkspaceRoot: string; readonly secretEnvelope: LocalSecretEnvelope; readonly providerReadinessProbe?: ProviderReadinessProbe; readonly feishuConnectionReadinessProbe?: FeishuReadinessProbe; } export async function registerOrgRoutes(app: FastifyInstance, config: OrgRouteConfig): Promise { const guardDeps: GuardDeps = { prisma: config.prisma, sessionSecret: config.sessionSecret }; app.get("/api/org/:orgSlug", async (request, reply) => { try { const { orgSlug } = request.params as { orgSlug: string }; const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug }); if (auth === null) return; return { organization: { id: auth.organization.id, slug: auth.organization.slug, name: auth.organization.name, status: auth.organization.status, }, actorRole: auth.membershipRole, }; } catch (err) { return handleRouteError(reply, err); } }); app.get("/api/org/:orgSlug/settings", async (request, reply) => { try { const { orgSlug } = request.params as { orgSlug: string }; const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug }); if (auth === null) return; const settings = await ensureOrganizationProjectSettings(config.prisma, auth.organization.id); return { membersCanCreateProjects: settings.membersCanCreateProjects, }; } catch (err) { return handleRouteError(reply, err); } }); app.patch("/api/org/:orgSlug/settings", async (request, reply) => { try { const { orgSlug } = request.params as { orgSlug: string }; const auth = await requireOrgRole(request, reply, guardDeps, { orgSlug }); if (auth === null) return; const body = request.body as { membersCanCreateProjects?: unknown }; if (typeof body.membersCanCreateProjects !== "boolean") { return reply.status(400).send({ error: { code: "bad_request", message: "membersCanCreateProjects must be a boolean" }, }); } const settings = await setMembersCanCreateProjects(config.prisma, { organizationId: auth.organization.id, enabled: body.membersCanCreateProjects, }); return { membersCanCreateProjects: settings.membersCanCreateProjects, }; } catch (err) { return handleRouteError(reply, err); } }); await registerExplorerRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, projectWorkspaceRoot: config.projectWorkspaceRoot, }); await registerMembersRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, }); await registerTeamsRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, }); await registerAccessRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, }); await registerCapacityRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, }); await registerAgentConfigRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, skillStoreRoot: readSkillStoreRoot(), secretEnvelope: config.secretEnvelope, }); await registerSessionsAndUsageRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, }); await registerProviderConnectionRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, secretEnvelope: config.secretEnvelope, ...(config.providerReadinessProbe !== undefined ? { providerReadinessProbe: config.providerReadinessProbe } : {}), }); await registerFeishuApplicationConnectionRoutes(app, { prisma: config.prisma, sessionSecret: config.sessionSecret, secretEnvelope: config.secretEnvelope, ...(config.feishuConnectionReadinessProbe !== undefined ? { readinessProbe: config.feishuConnectionReadinessProbe } : {}), }); }