mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-04 16:59:03 +02:00
refactor: standardize API error handling (#1635)
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
import type { HandleClientError } from '@sveltejs/kit';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { getAxiosErrorMessage, getAxiosErrorRequestId } from '$lib/utils/error-util';
|
||||
|
||||
export const handleError: HandleClientError = async ({ error, message, status }) => {
|
||||
if (error instanceof AxiosError) {
|
||||
message = error.response?.data.error || message;
|
||||
if (isAxiosError(error)) {
|
||||
message = getAxiosErrorMessage(error, message);
|
||||
status = error.response?.status || status;
|
||||
console.error(
|
||||
`Axios error: ${error.request.path} - ${error.response?.data.error ?? error.message}`
|
||||
`Axios error: ${error.request?.path ?? 'unknown path'} - ${getAxiosErrorMessage(error, error.message)}`,
|
||||
{ requestId: getAxiosErrorRequestId(error) }
|
||||
);
|
||||
} else {
|
||||
console.error(error);
|
||||
|
||||
@@ -1,17 +1,65 @@
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { WebAuthnError } from '@simplewebauthn/browser';
|
||||
import { AxiosError } from 'axios';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
interface ApiErrorResponse {
|
||||
error?: unknown;
|
||||
code?: string;
|
||||
request_id?: string;
|
||||
}
|
||||
|
||||
const codeMessages: Record<string, () => string> = {
|
||||
internal_error: () => m.an_unknown_error_occurred(),
|
||||
request_timeout: () => m.please_try_again(),
|
||||
rate_limited: () => m.please_try_again(),
|
||||
not_signed_in: () => m.please_try_to_sign_in_again(),
|
||||
reauthentication_required: () => m.please_try_to_sign_in_again(),
|
||||
invalid_webauthn_session: () => m.passkey_request_expired(),
|
||||
invalid_webauthn_response: () => m.passkey_response_invalid(),
|
||||
webauthn_authentication_failed: () => m.passkey_verification_failed(),
|
||||
passkey_user_verification_required: () => m.passkey_user_verification_required(),
|
||||
device_login_expired: () => m.device_login_request_expired()
|
||||
};
|
||||
|
||||
function getApiErrorResponse(e: unknown): ApiErrorResponse | undefined {
|
||||
if (!isAxiosError(e)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const data = e.response?.data;
|
||||
if (!data || typeof data !== 'object') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return data as ApiErrorResponse;
|
||||
}
|
||||
|
||||
export function getAxiosErrorMessage(
|
||||
e: unknown,
|
||||
defaultMessage: string = m.an_unknown_error_occurred()
|
||||
) {
|
||||
let message = defaultMessage;
|
||||
if (e instanceof AxiosError) {
|
||||
message = e.response?.data.error || message;
|
||||
const response = getApiErrorResponse(e);
|
||||
if (!response) {
|
||||
return defaultMessage;
|
||||
}
|
||||
return message;
|
||||
|
||||
const codeMessage = response.code ? codeMessages[response.code]?.() : undefined;
|
||||
return codeMessage || (typeof response.error === 'string' ? response.error : defaultMessage);
|
||||
}
|
||||
|
||||
export function getAxiosErrorRequestId(e: unknown): string | undefined {
|
||||
if (!isAxiosError(e)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const bodyRequestId = getApiErrorResponse(e)?.request_id;
|
||||
if (bodyRequestId) {
|
||||
return bodyRequestId;
|
||||
}
|
||||
|
||||
const headerRequestId = e.response?.headers?.['x-request-id'];
|
||||
return typeof headerRequestId === 'string' ? headerRequestId : undefined;
|
||||
}
|
||||
|
||||
export function axiosErrorToast(
|
||||
@@ -39,13 +87,14 @@ export function getWebauthnErrorMessage(e: unknown) {
|
||||
NotAllowedError: m.webauthn_operation_not_allowed_or_timed_out()
|
||||
};
|
||||
|
||||
const response = getApiErrorResponse(e);
|
||||
let message: string = m.an_unknown_error_occurred();
|
||||
if (e instanceof WebAuthnError && e.code in errors) {
|
||||
message = errors[e.code as keyof typeof errors];
|
||||
} else if (e instanceof WebAuthnError && e.cause instanceof Error && e.cause.name in errors) {
|
||||
message = errors[e.cause.name as keyof typeof errors];
|
||||
} else if (e instanceof AxiosError && e.response?.data.error) {
|
||||
message = e.response?.data.error;
|
||||
} else if (isAxiosError(e) && (response?.code || response?.error)) {
|
||||
message = getAxiosErrorMessage(e);
|
||||
} else {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import AppConfigService from '$lib/services/app-config-service';
|
||||
import UserService from '$lib/services/user-service';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import appConfigStore from '$lib/stores/application-configuration-store';
|
||||
import userStore from '$lib/stores/user-store';
|
||||
import { getAxiosErrorMessage, getAxiosErrorRequestId } from '$lib/utils/error-util';
|
||||
import { setLocaleForLibraries } from '$lib/utils/locale.util';
|
||||
import { getAuthRedirectPath } from '$lib/utils/redirection-util';
|
||||
import { setTracingEnabled } from '$lib/utils/tracing-util';
|
||||
@@ -17,8 +19,12 @@ export const load: LayoutLoad = async ({ url }) => {
|
||||
const userPromise = userService.getCurrent().catch(() => null);
|
||||
|
||||
const appConfigPromise = appConfigService.list().catch((e) => {
|
||||
const fallbackMessage = e instanceof Error ? e.message : m.an_unknown_error_occurred();
|
||||
console.error(
|
||||
`Failed to get application configuration: ${e.response?.data.error || e.message}`
|
||||
`Failed to get application configuration: ${getAxiosErrorMessage(e, fallbackMessage)}`,
|
||||
{
|
||||
requestId: getAxiosErrorRequestId(e)
|
||||
}
|
||||
);
|
||||
return null;
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import UserService from '$lib/services/user-service';
|
||||
import { preventDefault } from '$lib/utils/event-util';
|
||||
import { getAxiosErrorMessage } from '$lib/utils/error-util';
|
||||
import { fade } from 'svelte/transition';
|
||||
import LoginLogoErrorSuccessIndicator from '../../components/login-logo-error-success-indicator.svelte';
|
||||
|
||||
@@ -23,7 +24,7 @@
|
||||
await userService
|
||||
.requestOneTimeAccessEmailAsUnauthenticatedUser(email, data.redirect)
|
||||
.then(() => (success = true))
|
||||
.catch((e) => (error = e.response?.data.error || m.an_unknown_error_occurred()));
|
||||
.catch((e) => (error = getAxiosErrorMessage(e)));
|
||||
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user