Files
SRCmail/app/api/setup/status/route.ts
T

53 lines
2.0 KiB
TypeScript

import { NextResponse } from 'next/server';
import { detectSetupState } from '@/lib/setup/state';
import { authenticateWizardRequest } from '@/lib/setup/session';
import { configManager } from '@/lib/admin/config-manager';
import { isConfigReadOnly } from '@/lib/admin/paths';
import { SENSITIVE_CONFIG_KEYS } from '@/lib/admin/types';
export const dynamic = 'force-dynamic';
/**
* GET /api/setup/status - public endpoint that returns the wizard state
* and (if authenticated) the partial config saved by previous steps. The
* wizard polls this on load so a refresh resumes with prior values.
*
* Sensitive values (OAuth client secret, session secret) are NEVER sent
* back to the client - only a `<key>HasValue` boolean. Re-entering them
* after refresh is the price of not exposing them.
*/
export async function GET() {
await configManager.ensureLoaded();
const state = detectSetupState();
const authenticated = state === 'bootstrap' ? await authenticateWizardRequest() : false;
let partialConfig: Record<string, unknown> | null = null;
if (state === 'bootstrap' && authenticated) {
// Only echo back values the operator has actually saved during the
// wizard (admin overrides). System defaults must not flow back here,
// because the wizard has its own opinionated defaults (e.g. settings
// sync on by default) that we'd otherwise stomp.
const sources = configManager.getAllWithSources();
const safe: Record<string, unknown> = {};
for (const [key, info] of Object.entries(sources)) {
if (info.source !== 'admin') continue;
if (SENSITIVE_CONFIG_KEYS.has(key)) {
safe[`${key}HasValue`] = typeof info.value === 'string' && info.value.length > 0;
} else {
safe[key] = info.value;
}
}
partialConfig = safe;
}
return NextResponse.json(
{
state,
authenticated,
readOnly: isConfigReadOnly(),
partialConfig,
},
{ headers: { 'Cache-Control': 'no-store' } },
);
}