Files
warmbly/web/src/lib/api/client/normalizeError.ts
T
Matthew Meszaros 734cb5fe08 feat: make self-hosted onboarding survivable by fixing invite_only, which could not onboard anyone (the accept route is JWT-only, so redeeming the invitation that would create your account required already having one, making the self-host default silently identical to fully closed), threading the invitation token through registration so an invited person lands in the inviting organization instead of a stray workspace, gating SSO just-in-time provisioning behind DISABLE_REGISTRATION (it bypassed the gate entirely, so an instance set to true was still open to anyone the IdP would assert) with SSO_AUTO_PROVISION as the opt-out, correcting the OIDC redirect URL that pointed at /api/v1 against a route at /v1 and 404'd every SSO login, scoping the first-launch exemption so it no longer overrides an explicit lockdown, preserving the remaining TTL when restoring a losing setup token so a public endpoint cannot hold the claim window open forever, replacing a generic 403 with typed registration_invite_only, registration_closed, invitation_invalid, setup_token_invalid and setup_already_complete codes that name the next step, logging why no claim link was issued on an already-claimed instance instead of staying silent, adding a warmblyctl operator CLI (status with health checks and a non-zero exit, reissuable setup-link, user create/list/reset-password/grant-admin/revoke-admin/disable-2fa, hash-password) so a locked-out operator no longer needs hand-written psql, adding read-only instance configuration over 104 environment variables with structural secret redaction and fingerprints, 35 health checks, a database-backed settings tier for the three keys no environment variable owns, hiding the signup form when the config already says invite_only rather than failing the whole form with a toast, and documenting first run, accounts and access, configuration, instance health and troubleshooting alongside the root .env.example the README told operators to write but never shipped (#114)
2026-08-16 05:58:11 +02:00

64 lines
1.8 KiB
TypeScript

import axios from "axios";
import { AuthError } from "@/lib/errors/auth";
export interface AppError {
error: string;
message: string;
status?: number;
redirect?: boolean;
/** Stable machine-readable code from the API, for branching on a specific
* condition rather than matching on human-readable text. */
code?: string;
/** Correlation id the API already returns, so a user can quote it and an
* operator can find the matching server-side log line. */
request_id?: string;
}
export function normalizeError(error: unknown): AppError {
if (error instanceof AuthError) {
return {
error: "Authentication Required",
message: error.message,
status: 401,
redirect: true,
};
}
if (axios.isAxiosError(error)) {
if (!error.response) {
// network, CORS, or timeout
return {
error: "Network Error",
message: "Please check your connection.",
};
}
const status = error.response.status;
const data = error.response.data;
if (status === 401) {
return {
error: data.error || "Unauthorized",
message: data.message || "Your session is invalid or expired.",
status,
redirect: true,
code: data.code,
request_id: data.request_id,
};
}
return {
error: data.error || "Unknown Error",
message: data.message || "Unexpected error occured.",
status,
code: data.code,
request_id: data.request_id,
}
}
return {
error: "Unknown Error",
message: "Unexpected error occurred.",
};
}