import type { Db } from "@paperclipai/db"; import { companies, instanceSettings } from "@paperclipai/db"; import { instanceExperimentalSettingsSchema, type InstanceExperimentalSettings, type InstanceSettings, type PatchInstanceExperimentalSettings, } from "@paperclipai/shared"; import { eq } from "drizzle-orm"; const DEFAULT_SINGLETON_KEY = "default"; function normalizeExperimentalSettings(raw: unknown): InstanceExperimentalSettings { const parsed = instanceExperimentalSettingsSchema.safeParse(raw ?? {}); if (parsed.success) { return { enableIsolatedWorkspaces: parsed.data.enableIsolatedWorkspaces ?? false, }; } return { enableIsolatedWorkspaces: false, }; } function toInstanceSettings(row: typeof instanceSettings.$inferSelect): InstanceSettings { return { id: row.id, experimental: normalizeExperimentalSettings(row.experimental), createdAt: row.createdAt, updatedAt: row.updatedAt, }; } export function instanceSettingsService(db: Db) { async function getOrCreateRow() { const existing = await db .select() .from(instanceSettings) .where(eq(instanceSettings.singletonKey, DEFAULT_SINGLETON_KEY)) .then((rows) => rows[0] ?? null); if (existing) return existing; const now = new Date(); const [created] = await db .insert(instanceSettings) .values({ singletonKey: DEFAULT_SINGLETON_KEY, experimental: {}, createdAt: now, updatedAt: now, }) .onConflictDoUpdate({ target: [instanceSettings.singletonKey], set: { updatedAt: now, }, }) .returning(); return created; } return { get: async (): Promise => toInstanceSettings(await getOrCreateRow()), getExperimental: async (): Promise => { const row = await getOrCreateRow(); return normalizeExperimentalSettings(row.experimental); }, updateExperimental: async (patch: PatchInstanceExperimentalSettings): Promise => { const current = await getOrCreateRow(); const nextExperimental = normalizeExperimentalSettings({ ...normalizeExperimentalSettings(current.experimental), ...patch, }); const now = new Date(); const [updated] = await db .update(instanceSettings) .set({ experimental: { ...nextExperimental }, updatedAt: now, }) .where(eq(instanceSettings.id, current.id)) .returning(); return toInstanceSettings(updated ?? current); }, listCompanyIds: async (): Promise => db .select({ id: companies.id }) .from(companies) .then((rows) => rows.map((row) => row.id)), }; }