feat(12-06): expose non-secret DB name via GET /api/setup/status (gap 3)
- status returns { setupComplete, dbName } from process.env.DB_NAME (null fallback)
- only the DB name; never DB_HOST/DB_USER/DB_PASSWORD
- SetupStatusResponse carries dbName?: string | null for the PWA read-only field
This commit is contained in:
@@ -88,7 +88,10 @@ setupRouter.get('/status', async (c) => {
|
||||
// completion (D-10). Using it here keeps the status response in sync with the guard
|
||||
// without a second DB read pattern (covers setup_complete AND effective-config).
|
||||
const locked = await isSetupLocked();
|
||||
return c.json({ setupComplete: locked });
|
||||
// Gap 3 (backend): surface the NON-SECRET DB name so the PWA can render a read-only
|
||||
// field giving the "database connection verified" row an on-screen referent. Only the
|
||||
// database NAME is exposed — never DB_HOST/DB_USER/DB_PASSWORD (connection secrets/topology).
|
||||
return c.json({ setupComplete: locked, dbName: process.env.DB_NAME ?? null });
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -251,6 +251,23 @@ describe('GET /api/setup/status', () => {
|
||||
expect(body.setupComplete).toBe(false);
|
||||
});
|
||||
|
||||
// Gap 3 (backend): status exposes the NON-SECRET DB name as an on-screen referent
|
||||
// for the "database connection verified" row. No DB_HOST/DB_USER/DB_PASSWORD.
|
||||
it('returns the non-secret dbName from process.env.DB_NAME (gap 3)', async () => {
|
||||
process.env.DB_NAME = 'familysync_test';
|
||||
const app = await getApp();
|
||||
const res = await app.fetch(jsonRequest('GET', '/api/setup/status'));
|
||||
expect(res.status).toBe(200);
|
||||
const bodyText = await res.text();
|
||||
const body = JSON.parse(bodyText) as { setupComplete: boolean; dbName?: string | null };
|
||||
expect(body.dbName).toBe('familysync_test');
|
||||
// No connection secrets/topology may leak into the status response.
|
||||
const password = process.env.DB_PASSWORD;
|
||||
if (password) {
|
||||
expect(bodyText).not.toContain(password);
|
||||
}
|
||||
});
|
||||
|
||||
it('returns { setupComplete: true } after app_config.setup_complete is set', async () => {
|
||||
// Directly set setup_complete in DB (simulates completed setup)
|
||||
await db
|
||||
|
||||
@@ -537,6 +537,12 @@ export async function saveMyCredential(payload: SaveMyCredentialPayload): Promis
|
||||
*/
|
||||
export interface SetupStatusResponse {
|
||||
setupComplete: boolean;
|
||||
/**
|
||||
* Non-secret database name (process.env.DB_NAME) surfaced for the wizard's
|
||||
* read-only "database connection verified" referent (gap 3). Never includes
|
||||
* DB_HOST/DB_USER/DB_PASSWORD.
|
||||
*/
|
||||
dbName?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user