import type { AdminDto, AdminLoginResp, AdminSpaceDetail, AdminSpaceMember, AdminSpaceSummary, AdminSuspension, AdminUserDetail, AdminUserSummary, AuditLogPage, JobLogPage, Page, QueueActionResp, QueueSnapshot, StorageObjectPage, StorageOverview, SystemStatus, TaskActionResp, TaskDetail, TaskPage, } from './api-types' import { ApiError } from './api-types' export * from './api-types' const BASE = '/api/v1' interface Envelope { code: number message: string data: T | null error: string | null error_code: string | null } function qs(params?: Record): string { if (!params) { return '' } const search = new URLSearchParams() for (const [key, value] of Object.entries(params)) { if (value === undefined || value === null || value === '') { continue } search.set(key, String(value)) } const query = search.toString() return query ? `?${query}` : '' } async function request( method: string, path: string, options?: { params?: Record; body?: unknown }, ): Promise { const response = await fetch(`${BASE}${path}${qs(options?.params)}`, { method, credentials: 'include', headers: options?.body === undefined ? undefined : { 'content-type': 'application/json' }, body: options?.body === undefined ? undefined : JSON.stringify(options.body), }) const text = await response.text() let envelope: Envelope | null = null if (text) { try { envelope = JSON.parse(text) as Envelope } catch { envelope = null } } if (!response.ok) { throw new ApiError( response.status, envelope?.message ?? response.statusText, envelope?.error_code ?? null, ) } return (envelope?.data as T) ?? (undefined as T) } export const adminAuth = { login: (username: string, password: string) => request('POST', '/admin/auth/login', { body: { username, password } }), logout: () => request('POST', '/admin/auth/logout'), me: () => request('GET', '/admin/me'), changePassword: (currentPassword: string, newPassword: string) => request('PATCH', '/admin/me/password', { body: { current_password: currentPassword, new_password: newPassword }, }), } export const adminAccounts = { list: (params: { limit?: number; offset?: number }) => request>('GET', '/admin/admins', { params }), create: (body: { username: string display_name?: string password: string role: string }) => request('POST', '/admin/admins', { body }), update: ( id: string, body: { display_name?: string; role?: string; is_active?: boolean }, ) => request('PATCH', `/admin/admins/${id}`, { body }), remove: (id: string) => request('DELETE', `/admin/admins/${id}`), resetPassword: (id: string, newPassword: string) => request('POST', `/admin/admins/${id}/reset-password`, { body: { new_password: newPassword }, }), } export const adminUsers = { list: (params: { q?: string status?: string limit?: number offset?: number sort?: string }) => request>('GET', '/admin/users', { params }), get: (uid: string) => request('GET', `/admin/users/${uid}`), update: ( uid: string, body: { display_name?: string is_active?: boolean is_restricted?: boolean prohibit_login?: boolean }, ) => request('PATCH', `/admin/users/${uid}`, { body }), suspend: ( uid: string, body: { suspension_type: string; reason?: string; ends_at?: string }, ) => request('POST', `/admin/users/${uid}/suspend`, { body }), revokeSuspension: (uid: string, suspensionId: string) => request('POST', `/admin/users/${uid}/suspensions/${suspensionId}/revoke`), resetPassword: (uid: string, newPassword: string) => request('POST', `/admin/users/${uid}/reset-password`, { body: { new_password: newPassword }, }), revokeSessions: (uid: string) => request<{ revoked: number }>('POST', `/admin/users/${uid}/revoke-sessions`), remove: (uid: string) => request<{ user_id: string; anonymized: boolean }>('DELETE', `/admin/users/${uid}`), } export const adminSpaces = { list: (params: { q?: string status?: string owner_id?: string limit?: number offset?: number }) => request>('GET', '/admin/spaces', { params }), get: (id: string) => request('GET', `/admin/spaces/${id}`), update: ( id: string, body: { name?: string; description?: string | null; is_private?: boolean; is_active?: boolean }, ) => request('PATCH', `/admin/spaces/${id}`, { body }), transfer: (id: string, newOwnerId: string) => request('POST', `/admin/spaces/${id}/transfer`, { body: { new_owner_id: newOwnerId }, }), remove: (id: string) => request('DELETE', `/admin/spaces/${id}`), members: (id: string, params: { status?: string; limit?: number; offset?: number }) => request>('GET', `/admin/spaces/${id}/members`, { params }), removeMember: (id: string, uid: string) => request<{ space_id: string; user_id: string; removed: boolean }>( 'DELETE', `/admin/spaces/${id}/members/${uid}`, ), } export const adminStorage = { overview: (refresh?: boolean) => request('GET', '/admin/storage/overview', { params: { refresh } }), objects: (params: { prefix?: string; token?: string; limit?: number }) => request('GET', '/admin/storage/objects', { params }), downloadUrl: (key: string) => request<{ key: string; url: string; expires_in_secs: number }>( 'GET', '/admin/storage/download-url', { params: { key } }, ), remove: (key: string) => request<{ key: string; deleted: boolean }>('DELETE', '/admin/storage/objects', { params: { key }, }), } export const adminQueues = { list: () => request('GET', '/admin/queues'), requeueFailed: (name: string) => request('POST', `/admin/queues/${name}/requeue-failed`), purge: (name: string) => request('POST', `/admin/queues/${name}/purge`), } export const adminTasks = { list: (params: { source?: string state?: string q?: string limit?: number offset?: number }) => request('GET', '/admin/tasks', { params }), get: (source: string, id: string) => request('GET', `/admin/tasks/${source}/${id}`), retry: (source: string, id: string) => request('POST', `/admin/tasks/${source}/${id}/retry`), cancel: (source: string, id: string) => request('POST', `/admin/tasks/${source}/${id}/cancel`), remove: (source: string, id: string) => request('DELETE', `/admin/tasks/${source}/${id}`), } export const adminLogs = { audit: (params: { action?: string target_type?: string admin_id?: string limit?: number offset?: number }) => request('GET', '/admin/logs/audit', { params }), jobs: (params: { source?: string; limit?: number; offset?: number }) => request('GET', '/admin/logs/jobs', { params }), } export const adminSystem = { status: () => request('GET', '/admin/system/status'), }