diff --git a/src/app/api/debug-migration/route.ts b/src/app/api/debug-migration/route.ts index a8ec492..36ace09 100644 --- a/src/app/api/debug-migration/route.ts +++ b/src/app/api/debug-migration/route.ts @@ -6,29 +6,54 @@ export async function GET() { const auth = await requireFamily(); if (!auth.success) return NextResponse.json({ error: auth.error }, { status: auth.status }); + // Each probe is independent so one failure never blanks the whole diagnostic. + const out: Record = {}; + + // pgvector status — FIRST and standalone (this is what diagnoses the + // "could not access file vector" production error). pg_available_extensions + // reads the on-disk extension catalog and never loads the vector library, so + // it works even when the image is missing the pgvector binaries. try { - const migrations = await sql.unsafe( - `SELECT hash, created_at FROM __drizzle_migrations ORDER BY created_at DESC LIMIT 10` - ); - const circleTables = await sql.unsafe( - `SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND (tablename LIKE 'circle%' OR tablename = 'post_reports') ORDER BY tablename` - ); - // pgvector status: is the binary available in this Postgres image, and is - // the extension actually created? (diagnoses "could not access file vector") const vectorRows = await sql.unsafe( `SELECT name, default_version, installed_version FROM pg_available_extensions WHERE name = 'vector'` ); const v = vectorRows[0] as { default_version?: string; installed_version?: string } | undefined; - const pgvector = { - binaryAvailable: !!v, // false → wrong Postgres image (no pgvector) - installed: !!v?.installed_version, // false → needs CREATE EXTENSION vector + out.pgvector = { + binaryAvailable: !!v, // false → wrong Postgres image (no pgvector binaries) + installed: !!v?.installed_version, // false → needs CREATE EXTENSION vector availableVersion: v?.default_version ?? null, installedVersion: v?.installed_version ?? null, }; - return NextResponse.json({ migrations, circleTables, pgvector }); - } catch (err: unknown) { - return NextResponse.json({ error: err instanceof Error ? err.message : String(err) }); + } catch (e) { + out.pgvectorError = String(e); } + + // Does the error_events table exist yet? (admin Errors page depends on it) + try { + const r = await sql.unsafe(`SELECT to_regclass('public.error_events') AS reg`); + out.errorEventsExists = !!(r[0] as { reg?: string })?.reg; + } catch (e) { + out.errorEventsError = String(e); + } + + // Applied drizzle migrations (the journal lives in the "drizzle" schema). + try { + out.migrations = await sql.unsafe( + `SELECT hash, created_at FROM drizzle.__drizzle_migrations ORDER BY created_at DESC LIMIT 12` + ); + } catch (e) { + out.migrationsError = String(e); + } + + try { + out.circleTables = await sql.unsafe( + `SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND (tablename LIKE 'circle%' OR tablename = 'post_reports') ORDER BY tablename` + ); + } catch (e) { + out.circleTablesError = String(e); + } + + return NextResponse.json(out); } // One-shot: apply circles migration via the app DB connection (requires login)