92 lines
3.1 KiB
TypeScript
92 lines
3.1 KiB
TypeScript
type ApiErrorPayload = {
|
||
message?: string;
|
||
issues?: {
|
||
formErrors?: string[];
|
||
fieldErrors?: Record<string, string[] | undefined>;
|
||
};
|
||
};
|
||
|
||
type ApiLikeError = Error & {
|
||
statusCode?: number;
|
||
status?: number;
|
||
data?: ApiErrorPayload;
|
||
};
|
||
|
||
const apiFieldLabels: Record<string, string> = {
|
||
email: "Email",
|
||
password: "Пароль",
|
||
confirmPassword: "Повтор пароля",
|
||
title: "Заголовок",
|
||
body: "Текст",
|
||
status: "Статус",
|
||
startsAt: "Начало",
|
||
expiresAt: "Окончание"
|
||
};
|
||
|
||
function getApiFieldLabel(field: string) {
|
||
return apiFieldLabels[field] ?? field;
|
||
}
|
||
|
||
function getApiErrorMessage(error: ApiLikeError) {
|
||
const formErrors = Array.isArray(error.data?.issues?.formErrors)
|
||
? error.data!.issues!.formErrors.filter((entry) => typeof entry === "string" && entry.trim())
|
||
: [];
|
||
|
||
if (formErrors.length > 0) {
|
||
return formErrors.join("\n");
|
||
}
|
||
|
||
const fieldErrors = error.data?.issues?.fieldErrors;
|
||
if (fieldErrors && typeof fieldErrors === "object") {
|
||
const messages = Object.entries(fieldErrors)
|
||
.flatMap(([field, value]) =>
|
||
(Array.isArray(value) ? value : [])
|
||
.filter((entry) => typeof entry === "string" && entry.trim())
|
||
.map((entry) => `${getApiFieldLabel(field)}: ${entry}`)
|
||
);
|
||
|
||
if (messages.length > 0) {
|
||
return messages.join("\n");
|
||
}
|
||
}
|
||
|
||
const responseMessage = typeof error.data?.message === "string" ? error.data.message.trim() : "";
|
||
if (responseMessage) {
|
||
return responseMessage;
|
||
}
|
||
|
||
const statusCode = error.statusCode ?? error.status;
|
||
|
||
if (statusCode === 400) return "Проверьте правильность заполнения формы";
|
||
if (statusCode === 401) return "Неверный логин или пароль";
|
||
if (statusCode === 403) return "Недостаточно прав для выполнения действия";
|
||
if (statusCode === 404) return "Запрашиваемые данные не найдены";
|
||
if (statusCode === 409) return "Такая запись уже существует";
|
||
if (statusCode === 422) return "Не удалось обработать введённые данные";
|
||
if (statusCode && statusCode >= 500) return "Ошибка сервера. Попробуйте ещё раз позже";
|
||
|
||
return error.message || "Не удалось выполнить запрос";
|
||
}
|
||
|
||
export function useApi<T>(path: string, options: Parameters<typeof $fetch<T>>[1] = {}) {
|
||
const config = useRuntimeConfig();
|
||
const token = useState<string | null>("auth-token", () => null);
|
||
const baseUrl = process.server ? config.apiBaseInternal : config.public.apiBase;
|
||
|
||
const headers = new Headers(options.headers as HeadersInit | undefined);
|
||
if (!headers.has("Content-Type") && options.method && options.method !== "GET") {
|
||
headers.set("Content-Type", "application/json");
|
||
}
|
||
if (token.value) {
|
||
headers.set("Authorization", `Bearer ${token.value}`);
|
||
}
|
||
|
||
return $fetch<T>(`${baseUrl}${path}`, {
|
||
...options,
|
||
headers,
|
||
credentials: "include"
|
||
}).catch((error: ApiLikeError) => {
|
||
throw new Error(getApiErrorMessage(error));
|
||
});
|
||
}
|