From 9ceb0acac3db89fe0f42629249a2a6c0a8d8228b Mon Sep 17 00:00:00 2001 From: Zai Shi Date: Thu, 24 Jul 2025 17:39:38 -0700 Subject: [PATCH] added config override crud --- .../latest/internal/config-overrides/crud.tsx | 61 +++++++++++++++++++ .../src/interface/crud/config-overrides.ts | 33 ++++++++++ 2 files changed, 94 insertions(+) create mode 100644 apps/backend/src/app/api/latest/internal/config-overrides/crud.tsx create mode 100644 packages/stack-shared/src/interface/crud/config-overrides.ts diff --git a/apps/backend/src/app/api/latest/internal/config-overrides/crud.tsx b/apps/backend/src/app/api/latest/internal/config-overrides/crud.tsx new file mode 100644 index 000000000..fb3b9ce6a --- /dev/null +++ b/apps/backend/src/app/api/latest/internal/config-overrides/crud.tsx @@ -0,0 +1,61 @@ +import { getRenderedEnvironmentConfigQuery, overrideEnvironmentConfigOverride } from "@/lib/config"; +import { getPrismaClientForTenancy, rawQuery } from "@/prisma-client"; +import { createCrudHandlers } from "@/route-handlers/crud-handler"; +import { configOverridesCrud } from "@stackframe/stack-shared/dist/interface/crud/config-overrides"; +import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields"; +import { StatusError } from "@stackframe/stack-shared/dist/utils/errors"; +import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies"; + +export const configOverridesCrudHandlers = createLazyProxy(() => createCrudHandlers(configOverridesCrud, { + paramsSchema: yupObject({ + emailId: yupString().optional(), + }), + onRead: async ({ auth }) => { + return { + id: auth.project.id, + branch_id: auth.tenancy.branchId, + organization_id: auth.tenancy.organization?.id, + project_id: auth.project.id, + config: JSON.stringify(auth.tenancy.config), + }; + }, + onUpdate: async ({ auth, data }) => { + if (auth.tenancy.organization) { + throw new StatusError(StatusError.BadRequest, 'Organizational config overrides are not yet supported'); + } + + const prisma = await getPrismaClientForTenancy(auth.tenancy); + + if (data.config) { + let parsedConfig; + try { + parsedConfig = JSON.parse(data.config); + } catch (e) { + if (e instanceof SyntaxError) { + throw new StatusError(StatusError.BadRequest, 'Invalid config JSON'); + } + throw e; + } + await overrideEnvironmentConfigOverride({ + projectId: auth.project.id, + branchId: auth.tenancy.branchId, + environmentConfigOverrideOverride: parsedConfig, + tx: prisma, + }); + } + + const updatedConfig = await rawQuery(prisma, getRenderedEnvironmentConfigQuery({ + projectId: auth.project.id, + branchId: auth.tenancy.branchId, + })); + + return { + id: auth.project.id, + branch_id: auth.tenancy.branchId, + // @ts-expect-error: remove this once we support organizational config overrides + organization_id: auth.tenancy.organization?.id, + project_id: auth.project.id, + config: JSON.stringify(updatedConfig), + }; + }, +})); diff --git a/packages/stack-shared/src/interface/crud/config-overrides.ts b/packages/stack-shared/src/interface/crud/config-overrides.ts new file mode 100644 index 000000000..208f1f799 --- /dev/null +++ b/packages/stack-shared/src/interface/crud/config-overrides.ts @@ -0,0 +1,33 @@ +import { CrudTypeOf, createCrud } from "../../crud"; +import * as schemaFields from "../../schema-fields"; +import { yupObject } from "../../schema-fields"; + +export const configOverridesCrudAdminReadSchema = yupObject({ + project_id: schemaFields.yupString().defined(), + branch_id: schemaFields.yupString().defined(), + organization_id: schemaFields.yupString().optional(), + id: schemaFields.yupString().defined(), + config: schemaFields.yupString().defined(), +}).defined(); + +export const configOverridesCrudAdminUpdateSchema = yupObject({ + config: schemaFields.yupString().optional(), +}).defined(); + +export const configOverridesCrud = createCrud({ + adminReadSchema: configOverridesCrudAdminReadSchema, + adminUpdateSchema: configOverridesCrudAdminUpdateSchema, + docs: { + adminRead: { + summary: 'Get the current config overrides', + description: 'Get the current config overrides with the specified project id and branch id', + tags: ['Config'], + }, + adminUpdate: { + summary: 'Update the current config overrides', + description: 'Update the current config overrides with the specified project id and branch id', + tags: ['Config'], + }, + }, +}); +export type ConfigOverridesCrud = CrudTypeOf;