import { createHmac } from "node:crypto"; import { join, resolve } from "node:path"; import { registrationNotice } from "@dada/shared-contracts"; import { createApp } from "./app.js"; import { readBrowserSupportRelease } from "./browser-support.js"; import { defaultInstanceConfigPath, readConfiguredLocalDataRoot } from "./local-data-root.js"; import { ManagedStorage } from "./managed-storage.js"; import { LatestExportService } from "./latest-exports.js"; import { CreditService } from "./credits.js"; import { ProjectService } from "./projects.js"; import { RegistrationService } from "./registration.js"; import { RecentAssetService } from "./recent-assets.js"; import { MockResendAdapter } from "./resend-adapter.js"; import { readSecureConfigCandidate } from "./secure-config.js"; import { StructuredJsonlLogger } from "./structured-log.js"; import { attachApiSupervisorControl, initializeApiCredentialClients, receiveApiCredentials } from "./supervisor-channel.js"; import { ModelConfigurationService } from "./model-configuration.js"; import { MockAmapAdapter } from "./amap-adapter.js"; import { StickerReleaseService } from "./sticker-releases.js"; const credentialChannelEnabled = process.argv.includes("--dada-credential-stdin"); let registration: RegistrationService | undefined; let projects: ProjectService | undefined; let credits: CreditService | undefined; let storage: ManagedStorage | undefined; let latestExports: LatestExportService | undefined; let models: ModelConfigurationService | undefined; let recentAssets: RecentAssetService | undefined; let stickers: StickerReleaseService | undefined; const instanceConfigPath = process.env.DADA_INSTANCE_CONFIG_PATH ?? defaultInstanceConfigPath(); if (credentialChannelEnabled) { const clients = initializeApiCredentialClients(await receiveApiCredentials()); try { const derivePepper = (purpose: string) => createHmac("sha256", clients.adminAllowlistPepper) .update(`Dada/P0A/${purpose}/v1`, "utf8") .digest(); const dataRoot = readConfiguredLocalDataRoot(instanceConfigPath); const databasePath = join(dataRoot, "db", "dada.sqlite3"); registration = new RegistrationService({ adminAllowlistPepper: Buffer.from(clients.adminAllowlistPepper), challengePepper: derivePepper("challenge-pepper"), currentPrivacyNoticeVersion: registrationNotice.version, databasePath, invitePepper: derivePepper("invite-pepper"), resend: new MockResendAdapter(), sessionPepper: derivePepper("session-pepper"), }); projects = new ProjectService({ databasePath }); credits = new CreditService({ databasePath }); storage = new ManagedStorage({ dataRoot, databasePath }); stickers = new StickerReleaseService({ databasePath, storage }); latestExports = new LatestExportService({ databasePath, storage }); models = new ModelConfigurationService({ database: registration.database }); recentAssets = new RecentAssetService({ database: registration.database }); registration.applySecureConfig(readSecureConfigCandidate(instanceConfigPath)); } catch (error) { stickers?.close(); stickers = undefined; latestExports?.close(); latestExports = undefined; storage?.close(); storage = undefined; credits?.close(); credits = undefined; projects?.close(); projects = undefined; registration?.close(); registration = undefined; throw error; } finally { clients.adminAllowlistPepper.fill(0); } } const browserSupportRelease = readBrowserSupportRelease(resolve("RELEASE.json")); const app = await createApp({ amap: new MockAmapAdapter(), ...(browserSupportRelease ? { browserSupportRelease } : {}), ...(credits ? { credits } : {}), ...(latestExports ? { latestExports } : {}), ...(models ? { models } : {}), ...(projects ? { projects } : {}), ...(registration ? { registration } : {}), ...(recentAssets ? { recentAssets } : {}), ...(stickers ? { stickers } : {}), }); await app.listen({ host: "127.0.0.1", port: 43121, }); const controlPipeIndex = process.argv.indexOf("--dada-control-pipe"); if (controlPipeIndex >= 0) { const controlPipe = process.argv[controlPipeIndex + 1]; if (!controlPipe) throw new Error("Supervisor control pipe name is required."); const control = attachApiSupervisorControl(controlPipe, async () => { await app.close(); latestExports?.close(); credits?.close(); projects?.close(); registration?.close(); storage?.close(); stickers?.close(); }); try { const dataRoot = readConfiguredLocalDataRoot(instanceConfigPath); if (!storage) storage = new ManagedStorage({ dataRoot, databasePath: join(dataRoot, "db", "dada.sqlite3") }); const logger = new StructuredJsonlLogger({ component: "api", directory: join(dataRoot, "logs", "api"), onWriteFailure: () => { try { storage?.setLogAvailability(false); } finally { control.reportStatus("storage_unavailable"); } }, }); logger.write({ error_category: "none", status_category: "ready" }); } catch { control.reportStatus("storage_unavailable"); } }