Files
fotospiel-app/resources/js/admin/lib/apiError.ts
Codex Agent 2e4226a838 Checkout‑Registrierung validiert jetzt die E‑Mail‑Länge, und die Checkout‑Flows sind Paddle‑only: Stripe‑Endpoints/
Services/Helpers sind entfernt, API/Frontend angepasst, Tests auf Paddle umgestellt. Außerdem wurde die CSP gestrafft
  und Stripe‑Texte in den Abandoned‑Checkout‑Mails ersetzt.
2025-12-18 11:14:42 +01:00

93 lines
2.4 KiB
TypeScript

export class ApiError extends Error {
constructor(
message: string,
public readonly status?: number,
public readonly code?: string,
public readonly meta?: Record<string, unknown>,
) {
super(message);
this.name = 'ApiError';
}
}
export function isApiError(value: unknown): value is ApiError {
return value instanceof ApiError;
}
export function getApiErrorMessage(error: unknown, fallback: string): string {
if (isApiError(error)) {
if (error.message) {
return error.message;
}
if (error.status && error.status >= 500) {
return 'Der Server hat nicht reagiert. Bitte versuche es später erneut.';
}
return fallback;
}
if (error instanceof Error && error.message) {
return error.message;
}
return fallback;
}
export function getApiValidationMessage(error: unknown, fallback: string): string {
if (isApiError(error)) {
const errors = normalizeValidationErrors(error.meta);
if (errors.length) {
return errors.join('\n');
}
}
return getApiErrorMessage(error, fallback);
}
export type ApiErrorEventDetail = {
message: string;
status?: number;
code?: string;
meta?: Record<string, unknown>;
};
export const API_ERROR_EVENT = 'admin:api:error';
export function emitApiErrorEvent(detail: ApiErrorEventDetail): void {
if (typeof window === 'undefined') {
return;
}
window.dispatchEvent(new CustomEvent<ApiErrorEventDetail>(API_ERROR_EVENT, { detail }));
}
export function registerApiErrorListener(handler: (detail: ApiErrorEventDetail) => void): () => void {
if (typeof window === 'undefined') {
return () => {};
}
const listener = (event: Event) => {
const customEvent = event as CustomEvent<ApiErrorEventDetail>;
handler(customEvent.detail);
};
window.addEventListener(API_ERROR_EVENT, listener as EventListener);
return () => window.removeEventListener(API_ERROR_EVENT, listener as EventListener);
}
function normalizeValidationErrors(meta?: Record<string, unknown>): string[] {
if (!meta || typeof meta !== 'object') {
return [];
}
const errors = meta.errors;
if (!errors || typeof errors !== 'object') {
return [];
}
return Object.values(errors as Record<string, unknown>)
.flatMap((value) => (Array.isArray(value) ? value : [value]))
.filter((value): value is string => typeof value === 'string' && value.trim() !== '');
}