fix(12-04): align setup config client to camelCase API contract + readable errors
BUG 1: Rename SetupConfigPayload fields from snake_case to camelCase to match the API configSchema (appExternalUrl, oidcIssuer, oidcClientId, vapidPublicKey). Update SetupPage.tsx handleSaveAndValidate to send the correct camelCase keys. BUG 2: Extract human-readable message from ZodError object in postSetupConfig error handler. When body.error is an object with issues[], use issues[0].message instead of stringifying the object (which produces "[object Object]"). All 245 PWA tests pass; TypeScript clean.
This commit is contained in:
@@ -543,12 +543,15 @@ export interface SetupStatusResponse {
|
|||||||
* Payload for POST /api/setup/config.
|
* Payload for POST /api/setup/config.
|
||||||
* Collects non-secret runtime config written to the app_config table (D-02).
|
* Collects non-secret runtime config written to the app_config table (D-02).
|
||||||
* No secrets — VAPID private key and encryption key stay in Docker env.
|
* No secrets — VAPID private key and encryption key stay in Docker env.
|
||||||
|
*
|
||||||
|
* Field names match the API's configSchema exactly (camelCase).
|
||||||
|
* API contract: { oidcIssuer, oidcClientId, vapidPublicKey, appExternalUrl }
|
||||||
*/
|
*/
|
||||||
export interface SetupConfigPayload {
|
export interface SetupConfigPayload {
|
||||||
app_url: string;
|
appExternalUrl: string;
|
||||||
oidc_issuer: string;
|
oidcIssuer: string;
|
||||||
oidc_client_id: string;
|
oidcClientId: string;
|
||||||
vapid_public_key: string;
|
vapidPublicKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -594,8 +597,26 @@ export async function postSetupConfig(payload: SetupConfigPayload): Promise<void
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const body = (await res.json().catch(() => ({}))) as { error?: string };
|
const body = (await res.json().catch(() => ({}))) as {
|
||||||
throw new Error(body.error ?? `POST /api/setup/config failed: ${res.status}`);
|
error?: string | { name?: string; issues?: Array<{ message: string }> };
|
||||||
|
};
|
||||||
|
// body.error may be a ZodError object ({ name: "ZodError", issues: [...] })
|
||||||
|
// rather than a plain string — extract the first issue message to avoid
|
||||||
|
// "[object Object]" appearing in the UI (BUG 2 fix).
|
||||||
|
let message: string;
|
||||||
|
if (typeof body.error === 'string') {
|
||||||
|
message = body.error;
|
||||||
|
} else if (
|
||||||
|
body.error &&
|
||||||
|
typeof body.error === 'object' &&
|
||||||
|
Array.isArray((body.error as { issues?: unknown[] }).issues) &&
|
||||||
|
(body.error as { issues: Array<{ message: string }> }).issues.length > 0
|
||||||
|
) {
|
||||||
|
message = (body.error as { issues: Array<{ message: string }> }).issues[0].message;
|
||||||
|
} else {
|
||||||
|
message = `POST /api/setup/config failed: ${res.status}`;
|
||||||
|
}
|
||||||
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -502,10 +502,10 @@ function Step2Config({ onBack, onSuccess, stepHeadingRef }: Step2Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configMutation.mutate({
|
configMutation.mutate({
|
||||||
app_url: appUrl.trim(),
|
appExternalUrl: appUrl.trim(),
|
||||||
oidc_issuer: oidcIssuer.trim(),
|
oidcIssuer: oidcIssuer.trim(),
|
||||||
oidc_client_id: oidcClientId.trim(),
|
oidcClientId: oidcClientId.trim(),
|
||||||
vapid_public_key: vapidPublicKey.trim(),
|
vapidPublicKey: vapidPublicKey.trim(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user