all repos — weimar @ a8921d226b7ab5b6d366d88c51d3308362388cf7

Unnamed repository; edit this file 'description' to name the repository.

web/src/lib/api.ts (view raw)

  1const BASE = '/api';
  2
  3export interface User {
  4	id: number;
  5	username: string;
  6	is_admin: boolean;
  7	created_at: string;
  8	updated_at: string;
  9}
 10
 11export interface Image {
 12	id: number;
 13	filename: string;
 14	storage_path: string;
 15	thumbnail_path: string | null;
 16	uploaded_by: number;
 17	file_size: number;
 18	width: number;
 19	height: number;
 20	mime_type: string;
 21	created_at: string;
 22	updated_at: string;
 23	tags: string[];
 24}
 25
 26export interface ImageList {
 27	images: Image[];
 28	total: number;
 29}
 30
 31export interface Tag {
 32	tag: string;
 33	usage_count: number;
 34}
 35
 36export interface TagDetail {
 37	tag: string;
 38	usage_count: number;
 39}
 40
 41export interface ImageDetail extends Image {
 42	tags_detail: TagDetail[];
 43}
 44
 45export interface ApiError {
 46	error: string;
 47}
 48
 49async function request<T>(path: string, opts: RequestInit = {}): Promise<T> {
 50	const res = await fetch(BASE + path, {
 51		credentials: 'include',
 52		headers: opts.body instanceof FormData ? {} : { 'Content-Type': 'application/json' },
 53		...opts,
 54	});
 55	if (!res.ok) {
 56		const body = await res.json().catch(() => ({ error: res.statusText }));
 57		throw new Error((body as ApiError).error || `HTTP ${res.status}`);
 58	}
 59	return res.json();
 60}
 61
 62export async function login(username: string, password: string): Promise<User> {
 63	return request<User>('/auth/login', {
 64		method: 'POST',
 65		body: JSON.stringify({ username, password }),
 66	});
 67}
 68
 69export async function register(username: string, password: string): Promise<User> {
 70	return request<User>('/auth/register', {
 71		method: 'POST',
 72		body: JSON.stringify({ username, password }),
 73	});
 74}
 75
 76export async function logout(): Promise<void> {
 77	await request('/auth/logout', { method: 'POST' });
 78}
 79
 80export async function listImages(tags: string[] = [], page = 1, perPage = 50): Promise<ImageList> {
 81	const params = new URLSearchParams();
 82	params.set('page', String(page));
 83	params.set('per_page', String(perPage));
 84	for (const t of tags) {
 85		params.append('tags', t);
 86	}
 87	return request<ImageList>(`/images?${params}`);
 88}
 89
 90export async function getImage(id: number): Promise<ImageDetail> {
 91	return request<ImageDetail>(`/images/${id}`);
 92}
 93
 94export function imageDownloadUrl(id: number): string {
 95	return `${BASE}/images/${id}/download`;
 96}
 97
 98export function thumbnailUrl(id: number): string {
 99	return `${BASE}/images/${id}/thumbnail`;
100}
101
102export async function uploadImage(file: File, tags: string[]): Promise<Image> {
103	const fd = new FormData();
104	fd.append('file', file);
105	for (const t of tags) {
106		fd.append('tags', t);
107	}
108	return request<Image>('/upload', { method: 'POST', body: fd });
109}
110
111export async function suggestTags(query: string): Promise<Tag[]> {
112	return request<Tag[]>(`/tags/suggest?q=${encodeURIComponent(query)}`);
113}