128 lines
3.0 KiB
TypeScript
128 lines
3.0 KiB
TypeScript
import { AUTH_TOKEN_STORAGE_KEY } from "./storage";
|
|
|
|
const API_ROOT = import.meta.env.VITE_API_URL ?? (import.meta.env.DEV ? "http://localhost:8090/api" : "/api");
|
|
|
|
export class ApiError extends Error {
|
|
status: number;
|
|
|
|
constructor(message: string, status: number) {
|
|
super(message);
|
|
this.name = "ApiError";
|
|
this.status = status;
|
|
}
|
|
}
|
|
|
|
type ApiErrorPayload = {
|
|
message?: string;
|
|
error?: string;
|
|
detail?: string;
|
|
};
|
|
|
|
async function readErrorMessage(response: Response): Promise<string> {
|
|
const contentType = response.headers.get("content-type") ?? "";
|
|
|
|
if (contentType.includes("application/json")) {
|
|
const payload = (await response.json()) as ApiErrorPayload;
|
|
const message = payload.message?.trim() || payload.detail?.trim();
|
|
if (message) {
|
|
return message;
|
|
}
|
|
if (payload.error?.trim()) {
|
|
return `Anfrage fehlgeschlagen: ${payload.error.trim()}`;
|
|
}
|
|
}
|
|
|
|
const text = (await response.text()).trim();
|
|
if (text) {
|
|
return text;
|
|
}
|
|
|
|
return `Anfrage fehlgeschlagen (${response.status})`;
|
|
}
|
|
|
|
function authHeaders(): Record<string, string> {
|
|
if (typeof window === "undefined") {
|
|
return {};
|
|
}
|
|
|
|
const token = window.localStorage.getItem(AUTH_TOKEN_STORAGE_KEY);
|
|
if (!token) {
|
|
return {};
|
|
}
|
|
return { Authorization: `Bearer ${token}` };
|
|
}
|
|
|
|
async function handleResponse<T>(response: Response): Promise<T> {
|
|
if (!response.ok) {
|
|
throw new ApiError(await readErrorMessage(response), response.status);
|
|
}
|
|
if (response.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
const text = await response.text();
|
|
if (!text.trim()) {
|
|
return undefined as T;
|
|
}
|
|
return JSON.parse(text) as T;
|
|
}
|
|
|
|
export async function apiGet<T>(path: string): Promise<T> {
|
|
return handleResponse<T>(
|
|
await fetch(`${API_ROOT}${path}`, {
|
|
headers: authHeaders(),
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function apiPost<T>(path: string, body: unknown): Promise<T> {
|
|
return handleResponse<T>(
|
|
await fetch(`${API_ROOT}${path}`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...authHeaders(),
|
|
},
|
|
body: JSON.stringify(body),
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function apiPut<T>(path: string, body: unknown): Promise<T> {
|
|
return handleResponse<T>(
|
|
await fetch(`${API_ROOT}${path}`, {
|
|
method: "PUT",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...authHeaders(),
|
|
},
|
|
body: JSON.stringify(body),
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function apiPatch<T>(path: string, body: unknown): Promise<T> {
|
|
return handleResponse<T>(
|
|
await fetch(`${API_ROOT}${path}`, {
|
|
method: "PATCH",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...authHeaders(),
|
|
},
|
|
body: JSON.stringify(body),
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function apiDelete(path: string): Promise<void> {
|
|
await handleResponse<void>(
|
|
await fetch(`${API_ROOT}${path}`, {
|
|
method: "DELETE",
|
|
headers: authHeaders(),
|
|
}),
|
|
);
|
|
}
|
|
|
|
export function pdfUrl(sampleId: string): string {
|
|
return `${API_ROOT}/portal/reports/${sampleId}/pdf`;
|
|
}
|