This commit is contained in:
2026-03-28 01:06:30 -05:00
parent 796c374d38
commit ecb708790d
35 changed files with 2347 additions and 37 deletions

101
frontend/src/api/client.ts Normal file
View File

@@ -0,0 +1,101 @@
export interface Meme {
id: string;
title: string;
description: string | null;
file_path: string;
file_name: string;
file_size: number;
mime_type: string;
width: number;
height: number;
parent_id: string | null;
created_at: string;
tags: string[];
children?: Meme[];
}
export interface Tag {
id: number;
name: string;
meme_count: number;
}
export interface MemesResponse {
memes: Meme[];
total: number;
page: number;
limit: number;
}
export interface ListParams {
tag?: string;
q?: string;
page?: number;
limit?: number;
parent_only?: boolean;
}
async function apiFetch<T>(url: string, init?: RequestInit): Promise<T> {
const res = await fetch(url, init);
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(err.error ?? res.statusText);
}
return res.json() as Promise<T>;
}
export const api = {
memes: {
list(params: ListParams = {}): Promise<MemesResponse> {
const qs = new URLSearchParams();
if (params.tag) qs.set('tag', params.tag);
if (params.q) qs.set('q', params.q);
if (params.page) qs.set('page', String(params.page));
if (params.limit) qs.set('limit', String(params.limit));
if (params.parent_only !== undefined) qs.set('parent_only', String(params.parent_only));
return apiFetch<MemesResponse>(`/api/memes?${qs}`);
},
get(id: string): Promise<Meme & { children: Meme[] }> {
return apiFetch(`/api/memes/${id}`);
},
upload(formData: FormData): Promise<Meme> {
return apiFetch('/api/memes', { method: 'POST', body: formData });
},
update(id: string, body: { title?: string; description?: string; tags?: string[] }): Promise<Meme> {
return apiFetch(`/api/memes/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
},
delete(id: string): Promise<{ ok: boolean }> {
return apiFetch(`/api/memes/${id}`, { method: 'DELETE' });
},
rescale(id: string, body: { width?: number; height?: number; quality?: number; label?: string }): Promise<Meme> {
return apiFetch(`/api/memes/${id}/rescale`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
},
},
tags: {
list(): Promise<Tag[]> {
return apiFetch('/api/tags');
},
delete(id: number): Promise<{ ok: boolean }> {
return apiFetch(`/api/tags/${id}`, { method: 'DELETE' });
},
},
imageUrl(filePath: string): string {
return `/images/${filePath}`;
},
};