feat: dockerfile (node runtime), startup migration, README, runtime env config
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
// Idempotent migration runner. Run via `bun run migrate` / on container start.
|
||||
import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
// CLI migration runner (local / CI). Run via `bun run migrate`.
|
||||
// Production migrations run automatically via server/plugins/migrate.ts on startup.
|
||||
import postgres from "postgres";
|
||||
import { INIT_SQL } from "../utils/schema";
|
||||
|
||||
const url = process.env.DATABASE_URL;
|
||||
if (!url) {
|
||||
@@ -10,14 +9,10 @@ if (!url) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const sql = postgres(url, { max: 1 });
|
||||
|
||||
try {
|
||||
const ddl = readFileSync(join(here, "migrations", "0001_init.sql"), "utf8");
|
||||
// Trusted local DDL file, not user input.
|
||||
await sql.unsafe(ddl);
|
||||
console.log("migration 0001_init applied");
|
||||
await sql.unsafe(INIT_SQL);
|
||||
console.log("schema applied");
|
||||
} finally {
|
||||
await sql.end();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
export default defineEventHandler((event) => {
|
||||
if (!getRequestURL(event).pathname.startsWith("/api/")) return;
|
||||
|
||||
const origin = useRuntimeConfig().webOrigin;
|
||||
const origin = process.env.WEB_ORIGIN || "";
|
||||
if (origin) {
|
||||
setResponseHeader(event, "Access-Control-Allow-Origin", origin);
|
||||
setResponseHeader(event, "Vary", "Origin");
|
||||
|
||||
17
server/plugins/migrate.ts
Normal file
17
server/plugins/migrate.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { INIT_SQL } from "../utils/schema";
|
||||
|
||||
// Apply the schema idempotently on server startup. An advisory lock prevents concurrent
|
||||
// instances from racing on CREATE TABLE. Idempotent CREATE ... IF NOT EXISTS means this is
|
||||
// safe to run on every boot.
|
||||
export default defineNitroPlugin(async () => {
|
||||
try {
|
||||
const sql = getSql();
|
||||
await sql.begin(async (tx) => {
|
||||
await tx`select pg_advisory_xact_lock(871042)`;
|
||||
await tx.unsafe(INIT_SQL);
|
||||
});
|
||||
console.log("[migrate] schema ensured");
|
||||
} catch (e) {
|
||||
console.error("[migrate] failed:", (e as Error).message);
|
||||
}
|
||||
});
|
||||
@@ -45,14 +45,13 @@ function splitCsv(v: unknown): string[] {
|
||||
|
||||
let _cached: ReturnType<typeof makeVerifier> | null = null;
|
||||
|
||||
/** Process-wide verifier built from runtime config (Nitro server context). */
|
||||
/** Process-wide verifier built from environment (read at runtime, not baked at build). */
|
||||
export function getVerifier() {
|
||||
if (!_cached) {
|
||||
const c = useRuntimeConfig();
|
||||
_cached = makeVerifier({
|
||||
issuer: c.zitadelIssuer,
|
||||
audiences: splitCsv(c.zitadelAudience),
|
||||
allowedSubs: splitCsv(c.allowedUserIds),
|
||||
issuer: process.env.ZITADEL_ISSUER || "https://auth.kuns.dev",
|
||||
audiences: splitCsv(process.env.ZITADEL_AUDIENCE),
|
||||
allowedSubs: splitCsv(process.env.ALLOWED_USER_IDS),
|
||||
});
|
||||
}
|
||||
return _cached;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-- ClaudeDo Online Inbox — initial schema.
|
||||
-- Mirrors the desktop's Idle task backlog. Idempotent.
|
||||
|
||||
// Canonical schema for the Online Inbox. Single source of truth, applied idempotently by
|
||||
// the Nitro startup plugin (server/plugins/migrate.ts) and the CLI (server/db/migrate.ts).
|
||||
export const INIT_SQL = `
|
||||
create table if not exists lists (
|
||||
id text primary key, -- GUID supplied by the desktop, reused verbatim
|
||||
name text not null,
|
||||
@@ -20,3 +20,4 @@ create table if not exists tasks (
|
||||
|
||||
create index if not exists idx_tasks_list_id on tasks(list_id);
|
||||
create index if not exists idx_tasks_unconsumed on tasks(consumed) where consumed = false;
|
||||
`;
|
||||
Reference in New Issue
Block a user