77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
import type { Post, SiteConfig, Asset, ContactSubmission, Message } from './types';
|
|
|
|
async function apiFetch<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
const headers: Record<string, string> = {
|
|
...(options.headers as Record<string, string> || {}),
|
|
};
|
|
|
|
if (!(options.body instanceof FormData)) {
|
|
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
|
}
|
|
|
|
const res = await fetch(`/api${path}`, {
|
|
...options,
|
|
headers,
|
|
credentials: 'same-origin',
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const body = await res.json().catch(() => ({}));
|
|
throw new ApiError(res.status, body.error || res.statusText);
|
|
}
|
|
|
|
const text = await res.text();
|
|
return text ? JSON.parse(text) : (undefined as T);
|
|
}
|
|
|
|
export class ApiError extends Error {
|
|
constructor(public status: number, message: string) {
|
|
super(message);
|
|
this.name = 'ApiError';
|
|
}
|
|
}
|
|
|
|
// Auth
|
|
export const login = (token: string) =>
|
|
apiFetch<void>('/auth/login', { method: 'POST', body: JSON.stringify({ token }) });
|
|
export const logout = () => apiFetch<void>('/auth/logout', { method: 'POST' });
|
|
export const checkSession = () => apiFetch<void>('/auth/me');
|
|
|
|
// Posts
|
|
export const getPosts = () => apiFetch<Post[]>('/posts');
|
|
export const getPost = (slug: string) => apiFetch<Post>(`/posts/${encodeURIComponent(slug)}`);
|
|
export const savePost = (data: {
|
|
slug: string;
|
|
old_slug?: string | null;
|
|
title?: string | null;
|
|
date?: string;
|
|
summary?: string | null;
|
|
tags?: string[];
|
|
draft?: boolean;
|
|
content: string;
|
|
}) => apiFetch<Post>('/posts', { method: 'POST', body: JSON.stringify(data) });
|
|
export const deletePost = (slug: string) =>
|
|
apiFetch<void>(`/posts/${encodeURIComponent(slug)}`, { method: 'DELETE' });
|
|
|
|
// Config
|
|
export const getConfig = () => apiFetch<SiteConfig>('/config');
|
|
export const updateConfig = (data: Partial<SiteConfig>) =>
|
|
apiFetch<SiteConfig>('/config', { method: 'POST', body: JSON.stringify(data) });
|
|
|
|
// Assets
|
|
export const getAssets = () => apiFetch<Asset[]>('/uploads');
|
|
export const uploadAsset = (file: File) => {
|
|
const form = new FormData();
|
|
form.append('file', file);
|
|
return apiFetch<Asset>('/upload', { method: 'POST', body: form });
|
|
};
|
|
export const deleteAsset = (name: string) =>
|
|
apiFetch<void>(`/uploads/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
|
|
|
// Contact
|
|
export const submitContact = (data: ContactSubmission) =>
|
|
apiFetch<{ ok: boolean }>('/contact', { method: 'POST', body: JSON.stringify(data) });
|
|
export const listMessages = () => apiFetch<Message[]>('/messages');
|
|
export const deleteMessage = (id: string) =>
|
|
apiFetch<{ ok: boolean }>(`/messages/${encodeURIComponent(id)}`, { method: 'DELETE' });
|