feat(setup): install-wizard UI (epic 6c)
- Multi-step first-run wizard (intro → admin → Google → SMTP → finish), shown by App while the instance reports configured=false; the one-time token comes from the setup URL (?token=) and is sent as X-Setup-Token on every step. Google/SMTP steps appear only when secrets can be stored. - App holds the 'me' query until setup status is known (so it doesn't 503 against the setup lock); a missing/invalid token shows a 'use the link from the logs' screen. EN/HU/DE.
This commit is contained in:
parent
c33a04c353
commit
e4b997d754
6 changed files with 461 additions and 0 deletions
|
|
@ -210,6 +210,13 @@ export function setUnauthorizedHandler(fn: (() => void) | null): void {
|
|||
onUnauthorized = fn;
|
||||
}
|
||||
|
||||
// Headers for install-wizard calls: the one-time setup token plus the JSON content type (req
|
||||
// replaces — not merges — its default headers when opts.headers is given, so include both).
|
||||
const setupHeaders = (token: string) => ({
|
||||
"Content-Type": "application/json",
|
||||
"X-Setup-Token": token,
|
||||
});
|
||||
|
||||
async function req(url: string, opts: RequestInit = {}, cfg: ReqConfig = {}): Promise<any> {
|
||||
const method = opts.method ?? "GET";
|
||||
const canRetry = cfg.idempotent ?? method === "GET";
|
||||
|
|
@ -638,6 +645,19 @@ export const api = {
|
|||
// Public: which sign-in options this instance offers (e.g. hide Google when not configured).
|
||||
authConfig: (): Promise<{ google_enabled: boolean; allow_registration: boolean }> =>
|
||||
req("/auth/config"),
|
||||
|
||||
// --- first-run install wizard (token from the setup URL printed to the container logs) ---
|
||||
setupStatus: (): Promise<{ configured: boolean }> => req("/api/setup/status"),
|
||||
setupInfo: (token: string): Promise<{ secrets_manageable: boolean }> =>
|
||||
req("/api/setup/info", { headers: setupHeaders(token) }, { quiet: true }),
|
||||
setupAdmin: (token: string, email: string, password: string): Promise<{ ok: boolean }> =>
|
||||
req("/api/setup/admin", { method: "POST", headers: setupHeaders(token), body: JSON.stringify({ email, password }) }, { quiet: true }),
|
||||
setupConfig: (token: string, values: Record<string, unknown>): Promise<{ saved: string[] }> =>
|
||||
req("/api/setup/config", { method: "POST", headers: setupHeaders(token), body: JSON.stringify(values) }, { quiet: true }),
|
||||
setupTestEmail: (token: string, to: string): Promise<{ ok: boolean }> =>
|
||||
req("/api/setup/test-email", { method: "POST", headers: setupHeaders(token), body: JSON.stringify({ to }) }, { quiet: true }),
|
||||
setupFinish: (token: string): Promise<{ ok: boolean }> =>
|
||||
req("/api/setup/finish", { method: "POST", headers: setupHeaders(token) }, { quiet: true }),
|
||||
// --- auth: email + password (errors handled inline via quiet) ---
|
||||
register: (email: string, password: string): Promise<{ status: string }> =>
|
||||
req("/auth/register", { method: "POST", body: JSON.stringify({ email, password }) }, { quiet: true }),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue