import { config as loadEnv } from "dotenv"; import { eq } from "drizzle-orm"; loadEnv({ path: ".env.local" }); function readArg(name: string) { const prefix = `${name}=`; const match = process.argv.find((arg) => arg.startsWith(prefix)); return match ? match.slice(prefix.length) : null; } async function main() { const snapshotId = readArg("--snapshot-id"); const reason = readArg("--reason") || "manual-snapshot-restore"; if (!snapshotId) { throw new Error( "Usage: pnpm tsx scripts/system-settings/restore-snapshot.ts --snapshot-id= [--reason=]" ); } const dbModule = await import("../../lib/db"); const schemaModule = await import("../../lib/db/schema"); const snapshotModule = await import("../../lib/system-settings/snapshot"); const { db } = dbModule; const { siteSettings, systemSettingHistory, systemSettingSnapshot, systemSettings } = schemaModule; const { readSystemSettingSnapshotData } = snapshotModule; const [snapshot] = await db .select({ id: systemSettingSnapshot.id, scope: systemSettingSnapshot.scope, data: systemSettingSnapshot.data, }) .from(systemSettingSnapshot) .where(eq(systemSettingSnapshot.id, snapshotId)) .limit(1); if (!snapshot) { throw new Error(`Snapshot not found: ${snapshotId}`); } const parsedSnapshot = readSystemSettingSnapshotData( snapshot.scope as keyof typeof snapshotModule.SYSTEM_SETTING_SNAPSHOT_SCOPES, snapshot.data ); const keys = Object.keys(parsedSnapshot.systemSettings); await db.transaction(async (tx) => { for (const key of keys) { const [existing] = await tx .select({ value: systemSettings.value }) .from(systemSettings) .where(eq(systemSettings.key, key)) .limit(1); const nextValue = parsedSnapshot.systemSettings[key] ?? null; await tx.insert(systemSettingHistory).values({ key, previousValue: existing?.value ?? null, nextValue, reason, changedAt: new Date(), changedBy: null, }); await tx .insert(systemSettings) .values({ key, value: nextValue, updatedAt: new Date(), updatedBy: null, }) .onConflictDoUpdate({ target: systemSettings.key, set: { value: nextValue, updatedAt: new Date(), updatedBy: null, }, }); } if ("agentWorkflowDefaultModelId" in parsedSnapshot.siteSettings) { const [existingSiteSettings] = await tx.select().from(siteSettings).limit(1); const nextAgentWorkflowDefaultModelId = String(parsedSnapshot.siteSettings.agentWorkflowDefaultModelId || "").trim() || null; if (existingSiteSettings) { await tx .update(siteSettings) .set({ agentWorkflowDefaultModelId: nextAgentWorkflowDefaultModelId, updatedAt: new Date(), updatedBy: null, }) .where(eq(siteSettings.id, existingSiteSettings.id)); } else { await tx.insert(siteSettings).values({ siteName: "AI Chatbot", agentWorkflowDefaultModelId: nextAgentWorkflowDefaultModelId, updatedAt: new Date(), updatedBy: null, }); } } }); console.log( JSON.stringify( { snapshotId, scope: snapshot.scope, restoredKeyCount: keys.length, reason, }, null, 2 ) ); } main().catch((error) => { console.error(error); process.exit(1); });