mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-01 23:43:16 +01:00
48 lines
2.5 KiB
TypeScript
48 lines
2.5 KiB
TypeScript
/** Launcher-owned disposable database. The admin credential never enters the
|
|
* server/browser environment. Unknown remote cleanup keeps this database alive. */
|
|
import { randomUUID } from "node:crypto";
|
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
import { createDb } from "../../packages/db/src/index.js";
|
|
import { paperclipConfigSchema } from "../../packages/shared/src/config-schema.js";
|
|
import { recordControllerDatabaseOwnership } from "./instance-isolation.js";
|
|
import { prepareRunnerE2EServerConfig } from "./server-config.js";
|
|
|
|
export async function createExternalControllerDatabase(input: {
|
|
adminUrl: string; temporaryRoot: string; configPath: string; serverPort: number;
|
|
}) {
|
|
const url = new URL(input.adminUrl);
|
|
if (!["postgres:", "postgresql:"].includes(url.protocol)
|
|
|| !["127.0.0.1", "localhost", "[::1]"].includes(url.hostname)) {
|
|
throw new Error("E2E Postgres administrator must use an explicit loopback test server");
|
|
}
|
|
await (await prepareRunnerE2EServerConfig(input))?.close();
|
|
const config = paperclipConfigSchema.parse(JSON.parse(await readFile(input.configPath, "utf8")));
|
|
if (config.database.mode !== "embedded-postgres") throw new Error("E2E database fixture already configured");
|
|
const databaseName = `runner_e2e_${randomUUID().replaceAll("-", "")}`;
|
|
const admin = createDb(input.adminUrl, { maxConnections: 1 });
|
|
let created = false;
|
|
const cleanup = async () => {
|
|
if (!created) return;
|
|
const connection = createDb(input.adminUrl, { maxConnections: 1 });
|
|
try {
|
|
// No FORCE: a remaining controller keeps its evidence and fails cleanup.
|
|
await connection.$client.unsafe(`drop database "${databaseName}"`);
|
|
created = false;
|
|
} finally { await connection.$client.end({ timeout: 1 }); }
|
|
};
|
|
try {
|
|
await admin.$client.unsafe(`create database "${databaseName}"`);
|
|
created = true;
|
|
url.pathname = `/${databaseName}`;
|
|
config.database = { ...config.database, mode: "postgres", connectionString: url.toString() };
|
|
await mkdir(config.database.embeddedPostgresDataDir, { recursive: true, mode: 0o700 });
|
|
await recordControllerDatabaseOwnership({ ...input, dataDirectory: config.database.embeddedPostgresDataDir,
|
|
connectionString: url.toString(), externalDatabaseName: databaseName });
|
|
await writeFile(input.configPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
|
|
return { cleanup };
|
|
} catch (error) {
|
|
await cleanup();
|
|
throw error;
|
|
} finally { await admin.$client.end({ timeout: 1 }); }
|
|
}
|