all repos — weimar @ 0ba068faac3861b09c7dc896d508fa45e594e23e

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	avatar_url: string;
 19	file_size: number;
 20	width: number;
 21	height: number;
 22	mime_type: string;
 23	created_at: string;
 24	updated_at: string;
 25	tags: string[];
 26}
 27
 28export interface ImageList {
 29	images: Image[];
 30	total: number;
 31}
 32
 33export interface Tag {
 34	tag: string;
 35	usage_count: number;
 36}
 37
 38export interface TagDetail {
 39	tag: string;
 40	usage_count: number;
 41}
 42
 43export interface ImageDetail extends Image {
 44	tags_detail: TagDetail[];
 45	is_owner: boolean;
 46}
 47
 48export interface CurrentUser {
 49	id: number;
 50	username: string;
 51	is_admin: boolean;
 52	avatar_url: string;
 53	created_at: string;
 54	updated_at: string;
 55}
 56
 57export interface ApiError {
 58	error: string;
 59}
 60
 61async function request<T>(path: string, opts: RequestInit = {}): Promise<T> {
 62	const res = await fetch(BASE + path, {
 63		credentials: 'include',
 64		headers: opts.body instanceof FormData ? {} : { 'Content-Type': 'application/json' },
 65		...opts,
 66	});
 67	if (!res.ok) {
 68		const body = await res.json().catch(() => ({ error: res.statusText }));
 69		throw new Error((body as ApiError).error || `HTTP ${res.status}`);
 70	}
 71	return res.json();
 72}
 73
 74export async function login(username: string, password: string): Promise<User> {
 75	return request<User>('/auth/login', {
 76		method: 'POST',
 77		body: JSON.stringify({ username, password }),
 78	});
 79}
 80
 81export async function register(username: string, password: string): Promise<User> {
 82	return request<User>('/auth/register', {
 83		method: 'POST',
 84		body: JSON.stringify({ username, password }),
 85	});
 86}
 87
 88export async function logout(): Promise<void> {
 89	await request('/auth/logout', { method: 'POST' });
 90}
 91
 92export async function listImages(tags: string[] = [], uploader = '', page = 1, perPage = 50): Promise<ImageList> {
 93	const params = new URLSearchParams();
 94	params.set('page', String(page));
 95	params.set('per_page', String(perPage));
 96	for (const t of tags) {
 97		params.append('tags', t);
 98	}
 99	if (uploader) {
100		params.set('uploader', uploader);
101	}
102	return request<ImageList>(`/images?${params}`);
103}
104
105export async function getImage(id: number): Promise<ImageDetail> {
106	return request<ImageDetail>(`/images/${id}`);
107}
108
109export function imageDownloadUrl(id: number): string {
110	return `${BASE}/images/${id}/download`;
111}
112
113export function thumbnailUrl(id: number): string {
114	return `${BASE}/images/${id}/thumbnail`;
115}
116
117export async function uploadImage(file: File, tags: string[]): Promise<Image> {
118	const fd = new FormData();
119	fd.append('file', file);
120	for (const t of tags) {
121		fd.append('tags', t);
122	}
123	return request<Image>('/upload', { method: 'POST', body: fd });
124}
125
126export async function suggestTags(query: string): Promise<Tag[]> {
127	return request<Tag[]>(`/tags/suggest?q=${encodeURIComponent(query)}`);
128}
129
130export async function suggestUploaders(query: string): Promise<Tag[]> {
131	return request<Tag[]>(`/users/suggest?q=${encodeURIComponent(query)}`);
132}
133
134export async function uploadAvatar(file: File): Promise<{ avatar_url: string }> {
135	const fd = new FormData();
136	fd.append('avatar', file);
137	return request<{ avatar_url: string }>('/users/me/avatar', { method: 'POST', body: fd });
138}
139
140export async function getMe(): Promise<CurrentUser> {
141	return request<CurrentUser>('/users/me');
142}
143
144export interface SiteConfig {
145	site_title: string;
146	favicon_url?: string;
147	logo_url?: string;
148}
149
150export async function getConfig(): Promise<SiteConfig> {
151	return request<SiteConfig>('/config');
152}
153
154export async function addTags(imageId: number, tags: string[]): Promise<{ tags_detail: TagDetail[] }> {
155	return request<{ tags_detail: TagDetail[] }>(`/images/${imageId}/tags`, {
156		method: 'POST',
157		body: JSON.stringify({ tags }),
158	});
159}
160
161export async function removeTags(imageId: number, tags: string[]): Promise<{ tags_detail: TagDetail[] }> {
162	return request<{ tags_detail: TagDetail[] }>(`/images/${imageId}/tags`, {
163		method: 'DELETE',
164		body: JSON.stringify({ tags }),
165	});
166}