init
This commit is contained in:
91
frontend/composables/useApi.ts
Normal file
91
frontend/composables/useApi.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
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));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user