fix(web): read migration filenames through a definer instead of app_runtime
Independent Staging Quality Gate / validate (push) Successful in 19m18s
Independent Staging Quality Gate / publish (push) Successful in 33m22s

Staging web never became healthy because /api/health selected migration.schema_migrations as app_runtime, which is forbidden, so Docker rolled the image back.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-26 20:21:49 +08:00
co-authored by Cursor
parent fa2b1be852
commit 60cbe8a75e
5 changed files with 69 additions and 13 deletions
+20 -10
View File
@@ -1,4 +1,5 @@
import { NextResponse } from "next/server";
import { Pool } from "pg";
import { getTruthSourceRuntimeIdentity } from "@/lib/truth-source-runtime-identity";
import { loadLanguageModelCatalog } from "@/lib/model-catalog";
import {
@@ -68,12 +69,17 @@ async function rectificationMigrationCheck(): Promise<{
return { check: { status: "blocked", message: "missing:APP_DATABASE_URL" }, database: empty };
}
const started = Date.now();
const { Client } = await import("pg");
const client = new Client({ connectionString: url, connectionTimeoutMillis: 2000 });
const pool = new Pool({
connectionString: url,
max: 1,
connectionTimeoutMillis: 2000,
idleTimeoutMillis: 1000,
allowExitOnIdle: true,
application_name: "jyotisha-health",
});
try {
await client.connect();
const result = await client.query<{ filename: string }>(
"select filename from migration.schema_migrations order by filename",
const result = await pool.query<{ filename: string }>(
"select filename from public.rectification_schema_migration_filenames()",
);
const database = databaseHealthFromFilenames(result.rows.map((row) => row.filename));
return {
@@ -89,14 +95,14 @@ async function rectificationMigrationCheck(): Promise<{
} catch (error) {
return {
check: {
status: "blocked",
status: "degraded",
message: error instanceof Error ? error.name : "database_migration_query_failed",
latencyMs: Date.now() - started,
},
database: empty,
};
} finally {
await client.end().catch(() => undefined);
await pool.end().catch(() => undefined);
}
}
@@ -132,13 +138,17 @@ export async function GET() {
supabasePublicConfig: envCheck(["NEXT_PUBLIC_SUPABASE_URL", "NEXT_PUBLIC_SUPABASE_ANON_KEY"]),
supabaseServiceRole: envCheck(["SUPABASE_SERVICE_ROLE_KEY"]),
};
const migrations = await rectificationMigrationCheck();
const [migrations, modelCatalog, jyotishApi] = await Promise.all([
rectificationMigrationCheck(),
modelCatalogCheck(),
jyotishApiCheck(),
]);
const checks = {
web: { status: "ok" } satisfies Check,
...databaseChecks,
modelProviderEncryption: envCheck(["MODEL_PROVIDER_CONFIG_ENCRYPTION_KEY"]),
modelCatalog: await modelCatalogCheck(),
jyotishApi: await jyotishApiCheck(),
modelCatalog,
jyotishApi,
rectificationMigrations: migrations.check,
researchTruthSource: {
status: truthSourceIdentity.status,