shoc-frontend-new/src/api/api.ts

114 lines
3.1 KiB
TypeScript

import ky, { type HTTPError, type KyInstance, type Options } from "ky";
import { env } from "@/lib/env";
import { resolveApiPrefixUrl } from "@/lib/resolve-api-prefix-url";
import { API_PATHS } from "@/api/api-paths";
import { createApiErrorFromResponse, mapHttpStatusToMessage } from "@/api/api-error";
import { queryKeys } from "@/infra/query-key/query-key";
import queryClient from "@/lib/query/query-client";
import { isAuthBypassEnabled } from "@/lib/auth/auth-bypass";
import { clearAuth, getAccessToken } from "@/lib/auth/auth-storage";
function clearSessionAndRedirectToLogin(): void {
clearAuth();
queryClient.setQueryData(queryKeys.auth.session(), null);
if (typeof window !== "undefined" && window.location.pathname !== "/login") {
window.location.replace("/login");
}
}
function isAuthLoginRequest(request: Request): boolean {
return request.url.includes(API_PATHS.authentication.login);
}
export const api: KyInstance = ky.create({
prefixUrl: resolveApiPrefixUrl(env.apiUrl),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
hooks: {
beforeRequest: [
(request) => {
const token = getAccessToken();
if (token) {
request.headers.set("Authorization", token);
}
},
],
beforeError: [
async (error: HTTPError) => {
const { response } = error;
if (!response) {
error.message = "No response from server. Please check your connection.";
return error;
}
const apiError = await createApiErrorFromResponse(response);
error.message = apiError.message;
return error;
},
],
afterResponse: [
async (request, _options, response) => {
if (response.status !== 401 || isAuthLoginRequest(request) || isAuthBypassEnabled()) {
return response;
}
clearSessionAndRedirectToLogin();
return response;
},
],
},
});
export async function apiGet<T>(url: string, options?: Options): Promise<T> {
return api.get(url, options).json<T>();
}
export async function apiPost<T>(url: string, body?: unknown, options?: Options): Promise<T> {
return api.post(url, { ...options, json: body }).json<T>();
}
export async function apiPut<T>(url: string, body?: unknown, options?: Options): Promise<T> {
return api.put(url, { ...options, json: body }).json<T>();
}
export async function apiDelete<T>(url: string, options?: Options): Promise<T> {
return api.delete(url, options).json<T>();
}
export async function apiPostNoContent(
url: string,
body?: unknown,
options?: Options,
): Promise<void> {
const response = await api.post(url, { ...options, json: body });
if (response.status === 204) {
return;
}
const text = await response.text();
if (!text.trim()) {
return;
}
JSON.parse(text);
}
export async function apiDeleteNoContent(url: string, options?: Options): Promise<void> {
const response = await api.delete(url, options);
if (response.status === 204) {
return;
}
const text = await response.text();
if (!text.trim()) {
return;
}
JSON.parse(text);
}
export { mapHttpStatusToMessage };