105 lines
3.4 KiB
JavaScript
105 lines
3.4 KiB
JavaScript
import { spawnSync } from "node:child_process";
|
|
import { resolve } from "node:path";
|
|
|
|
export const REQUIRED_TABLES = [
|
|
"users",
|
|
"sessions",
|
|
"workspaces",
|
|
"workspace_members",
|
|
"notebooks",
|
|
"memos",
|
|
"mobile_sync_changes",
|
|
];
|
|
|
|
export const buildSchemaVerificationSql = () =>
|
|
`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${REQUIRED_TABLES.map((name) => `'${name}'`).join(", ")}) ORDER BY name`;
|
|
|
|
export const parseJsonOutput = (output, description) => {
|
|
if (!output.trim()) {
|
|
throw new Error(`Wrangler returned no JSON while checking ${description}.`);
|
|
}
|
|
|
|
try {
|
|
return JSON.parse(output);
|
|
} catch (error) {
|
|
throw new Error(
|
|
`Wrangler returned invalid JSON while checking ${description}: ${error instanceof Error ? error.message : String(error)}`,
|
|
);
|
|
}
|
|
};
|
|
|
|
export const parseD1Rows = (output) => {
|
|
// Some Wrangler versions emit an empty stdout for a successful D1 query
|
|
// with zero rows instead of the documented empty JSON result.
|
|
if (!output.trim()) {
|
|
return [];
|
|
}
|
|
const parsed = parseJsonOutput(output, "the remote D1 schema");
|
|
const results = Array.isArray(parsed) ? parsed : [parsed];
|
|
return results.flatMap((result) => result?.results ?? []);
|
|
};
|
|
|
|
export const parseSecretNames = (output) => {
|
|
const parsed = parseJsonOutput(output, "Worker Secrets");
|
|
const secrets = Array.isArray(parsed) ? parsed : parsed?.secrets ?? parsed?.result ?? [];
|
|
return new Set(secrets.map((secret) => secret?.name).filter(Boolean));
|
|
};
|
|
|
|
const runWrangler = (args, options = {}) => {
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
[resolve("scripts/run-wrangler.mjs"), ...args],
|
|
{ encoding: "utf8", env: process.env },
|
|
);
|
|
|
|
if (result.status !== 0) {
|
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
throw new Error(`Wrangler verification command exited with status ${result.status ?? 1}.`);
|
|
}
|
|
|
|
if (!result.stdout.trim() && !options.allowEmptyOutput) {
|
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
throw new Error(
|
|
"Wrangler completed without returning verification data. Check the D1 binding and deployment credentials, then retry.",
|
|
);
|
|
}
|
|
|
|
return result.stdout;
|
|
};
|
|
|
|
const main = () => {
|
|
const schemaOutput = runWrangler([
|
|
"d1",
|
|
"execute",
|
|
"DB",
|
|
"--remote",
|
|
"--command",
|
|
buildSchemaVerificationSql(),
|
|
"--json",
|
|
], { allowEmptyOutput: true });
|
|
const tableNames = new Set(parseD1Rows(schemaOutput).map((row) => row.name));
|
|
const missingTables = REQUIRED_TABLES.filter((table) => !tableNames.has(table));
|
|
if (missingTables.length > 0) {
|
|
throw new Error(
|
|
`Remote D1 migrations are incomplete; missing tables: ${missingTables.join(", ")}. Run bun run db:migrate:remote and retry deployment.`,
|
|
);
|
|
}
|
|
console.log(`[ok] remote D1 schema: ${REQUIRED_TABLES.length} required tables`);
|
|
|
|
const secretNames = parseSecretNames(runWrangler(["secret", "list", "--format", "json"]));
|
|
if (!secretNames.has("EDGE_EVER_AUTH_PASSWORD") && !secretNames.has("EDGE_EVER_AUTH_PASSWORD_HASH")) {
|
|
throw new Error("The deployed Worker has no EdgeEver authentication Secret.");
|
|
}
|
|
console.log("[ok] Worker authentication Secret is deployed");
|
|
};
|
|
|
|
if (import.meta.main) {
|
|
try {
|
|
main();
|
|
} catch (error) {
|
|
console.error(`[fail] ${error instanceof Error ? error.message : String(error)}`);
|
|
process.exit(1);
|
|
}
|
|
}
|