all repos — weimar @ 74fc3e1e24b47e370bcf5ea6b4e016cb50630920

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