Assist_Design/apps/portal/src/lib/utils/error-handling.ts

152 lines
3.4 KiB
TypeScript
Raw Normal View History

/**
* Standardized Error Handling for Portal
* Provides consistent error handling and user-friendly messages
*/
export interface ApiError {
success: false;
error: {
code: string;
message: string;
details?: Record<string, unknown>;
};
timestamp: string;
path: string;
}
export interface ApiErrorInfo {
code: string;
message: string;
shouldLogout?: boolean;
shouldRetry?: boolean;
}
/**
* Extract error information from various error types
*/
export function getErrorInfo(error: unknown): ApiErrorInfo {
// Handle API errors with structured format
if (isApiError(error)) {
return {
code: error.error.code,
message: error.error.message,
shouldLogout: shouldLogoutForError(error.error.code),
shouldRetry: shouldRetryForError(error.error.code),
};
}
// Handle fetch/network errors
if (error instanceof Error) {
if (error.name === 'TypeError' && error.message.includes('fetch')) {
return {
code: 'NETWORK_ERROR',
message: 'Unable to connect to the server. Please check your internet connection and try again.',
shouldRetry: true,
};
}
if (error.name === 'AbortError') {
return {
code: 'REQUEST_TIMEOUT',
message: 'The request timed out. Please try again.',
shouldRetry: true,
};
}
return {
code: 'UNKNOWN_ERROR',
message: 'An unexpected error occurred. Please try again.',
shouldRetry: true,
};
}
// Fallback for unknown error types
return {
code: 'UNKNOWN_ERROR',
message: 'An unexpected error occurred. Please try again.',
shouldRetry: true,
};
}
export function isApiError(error: unknown): error is ApiError {
return (
typeof error === 'object' &&
error !== null &&
'success' in error &&
error.success === false &&
'error' in error &&
typeof (error as any).error === 'object' &&
'code' in (error as any).error &&
'message' in (error as any).error
);
}
/**
* Determine if the user should be logged out for this error
*/
function shouldLogoutForError(code: string): boolean {
const logoutCodes = [
'TOKEN_REVOKED',
'INVALID_REFRESH_TOKEN',
'UNAUTHORIZED',
'SESSION_EXPIRED',
];
return logoutCodes.includes(code);
}
/**
* Determine if the request should be retried for this error
*/
function shouldRetryForError(code: string): boolean {
const noRetryCodes = [
'INVALID_CREDENTIALS',
'FORBIDDEN',
'ADMIN_REQUIRED',
'ACCOUNT_LOCKED',
'VALIDATION_ERROR',
'ACCOUNT_ALREADY_LINKED',
'ACCOUNT_EXISTS',
'CUSTOMER_NOT_FOUND',
'INVALID_REQUEST',
];
return !noRetryCodes.includes(code);
}
/**
* Get a user-friendly error message for display in UI
*/
export function getUserFriendlyMessage(error: unknown): string {
const errorInfo = getErrorInfo(error);
return errorInfo.message;
}
/**
* Handle authentication errors consistently
*/
export function handleAuthError(error: unknown, logout: () => void): void {
const errorInfo = getErrorInfo(error);
if (errorInfo.shouldLogout) {
logout();
}
}
/**
* Create a standardized error for logging
*/
export function createErrorLog(error: unknown, context: string): {
context: string;
code: string;
message: string;
timestamp: string;
} {
const errorInfo = getErrorInfo(error);
return {
context,
code: errorInfo.code,
message: errorInfo.message,
timestamp: new Date().toISOString(),
};
}