118 lines
4.7 KiB
TypeScript
118 lines
4.7 KiB
TypeScript
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";
|
|
|
|
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;
|
|
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 });
|
|
latestExports = new LatestExportService({ databasePath, storage });
|
|
models = new ModelConfigurationService({ database: registration.database });
|
|
recentAssets = new RecentAssetService({ database: registration.database });
|
|
registration.applySecureConfig(readSecureConfigCandidate(instanceConfigPath));
|
|
} catch (error) {
|
|
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({
|
|
...(browserSupportRelease ? { browserSupportRelease } : {}),
|
|
...(credits ? { credits } : {}),
|
|
...(latestExports ? { latestExports } : {}),
|
|
...(models ? { models } : {}),
|
|
...(projects ? { projects } : {}),
|
|
...(registration ? { registration } : {}),
|
|
...(recentAssets ? { recentAssets } : {}),
|
|
});
|
|
|
|
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();
|
|
});
|
|
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");
|
|
}
|
|
}
|