feat(web): add authenticated app shell
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
const apiBase = (): string => import.meta.env.PUBLIC_API_URL || '';
|
||||
|
||||
export function apiUrl(path: string): string {
|
||||
return path.startsWith('http') ? path : `${apiBase()}${path}`;
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
status: number;
|
||||
code?: string;
|
||||
detail?: string;
|
||||
|
||||
constructor(status: number, title: string, code?: string, detail?: string) {
|
||||
super(title);
|
||||
this.name = 'ApiError';
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
this.detail = detail;
|
||||
}
|
||||
}
|
||||
|
||||
export async function toApiError(res: Response): Promise<ApiError> {
|
||||
try {
|
||||
const body = await res.json();
|
||||
return new ApiError(
|
||||
res.status,
|
||||
body.title || body.detail || `Request failed (${res.status})`,
|
||||
body.code,
|
||||
body.detail,
|
||||
);
|
||||
} catch {
|
||||
return new ApiError(res.status, `Request failed (${res.status})`);
|
||||
}
|
||||
}
|
||||
|
||||
export function jsonHeaders(options?: RequestInit): Headers {
|
||||
const headers = new Headers(options?.headers);
|
||||
if (!headers.has('Content-Type') && !(options?.body instanceof FormData)) {
|
||||
headers.set('Content-Type', 'application/json');
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
export async function apiRequest<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
const res = await fetch(apiUrl(path), {
|
||||
...options,
|
||||
headers: jsonHeaders(options),
|
||||
});
|
||||
if (!res.ok) throw await toApiError(res);
|
||||
if (res.status === 204) return undefined as T;
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
Reference in New Issue
Block a user