all repos — weimar @ 453d1fa348ab96e179dc278cf0d13e8f9d0c7121

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