import type { AdminActRunPage, AdminActSummary, AdminChatAttachment, AdminChatMessage, AdminChatMessageDetail, AdminChatRoomDetail, AdminChatRoomMember, AdminChatRoomSummary, AdminChatStats, AdminDto, AdminLoginResp, AdminRepoPage, AdminRepoSummary, AdminSpaceDetail, AdminSpaceMember, AdminSpaceSummary, AdminSuspension, AdminUserDetail, AdminUserSecurity, AdminUserSessionPage, AdminUserSummary, AuditLogPage, BulkChatMessageResp, BulkTaskResp, BulkUsersResp, CreateUserResp, DeleteChatAttachmentResp, DeleteChatRoomResp, Dashboard, JobLogPage, Page, QueueActionResp, PurgeChatMessagesResp, QueueSnapshot, RunnerPage, RunnerSummary, RunnerTokenSummary, CreatedRunnerToken, EnvironmentAdminSummary, RemoveChatMemberResp, StorageObjectPage, StorageOverview, SystemConfig, SystemStatus, TaskActionResp, TaskDetail, TaskPage, TaskStats, UpdateAdminRepoCmd, } 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 function csvDownload(path: string, params?: Record): void { const link = document.createElement('a') link.href = `${BASE}${path}${qs(params)}` link.rel = 'noopener' document.body.append(link) link.click() link.remove() } 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 }, }), revokeSessions: (id: string) => request('POST', `/admin/admins/${id}/revoke-sessions`), } 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}`), create: (body: { username: string email: string password: string display_name?: string is_active?: boolean email_activated?: boolean }) => request('POST', '/admin/users', { body }), bulk: (body: { action: string ids: string[] reason?: string suspension_type?: string ends_at?: string }) => request('POST', '/admin/users/bulk', { body }), sessions: (uid: string, params: { kind?: string; include_revoked?: boolean; limit?: number; offset?: number }) => request('GET', `/admin/users/${uid}/sessions`, { params }), revokeSession: (uid: string, sessionId: string) => request('DELETE', `/admin/users/${uid}/sessions/${sessionId}`), security: (uid: string) => request('GET', `/admin/users/${uid}/security`), clearTwoFactor: (uid: string) => request('DELETE', `/admin/users/${uid}/two-factor`), exportCsv: (params: { q?: string; status?: string; sort?: string }) => csvDownload('/admin/users/export', params), } 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 allow_join?: boolean allow_invite?: boolean member_must_enable_2fa?: 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}`, ), addMember: (id: string, userId: string, aliasName?: string) => request<{ space_id: string; user_id: string; status: string; added: boolean }>( 'POST', `/admin/spaces/${id}/members`, { body: { user_id: userId, alias_name: aliasName } }, ), } export interface ChatRoomQuery extends Record { q?: string space_id?: string kind?: string status?: string visibility?: string limit?: number offset?: number } export interface ChatMessageQuery extends Record { room_id?: string author_id?: string status?: string q?: string from?: string to?: string limit?: number offset?: number } export interface ChatAttachmentQuery extends Record { room_id?: string user_id?: string status?: string q?: string limit?: number offset?: number } export const adminChat = { stats: () => request('GET', '/admin/chat/stats'), rooms: (params: ChatRoomQuery) => request>('GET', '/admin/chat/rooms', { params }), room: (id: string) => request('GET', `/admin/chat/rooms/${id}`), updateRoom: ( id: string, body: { name?: string topic?: string is_private?: boolean nsfw?: boolean is_active?: boolean }, ) => request('PATCH', `/admin/chat/rooms/${id}`, { body }), removeRoom: (id: string) => request('DELETE', `/admin/chat/rooms/${id}`), members: (id: string, params: { status?: string; limit?: number; offset?: number }) => request>('GET', `/admin/chat/rooms/${id}/members`, { params }), removeMember: (id: string, uid: string) => request('DELETE', `/admin/chat/rooms/${id}/members/${uid}`), purgeRoom: (id: string) => request('POST', `/admin/chat/rooms/${id}/purge`), messages: (params: ChatMessageQuery) => request>('GET', '/admin/chat/messages', { params }), message: (id: string) => request('GET', `/admin/chat/messages/${id}`), bulkDelete: (body: { message_ids: string[]; reason?: string }) => request('POST', '/admin/chat/messages/bulk-delete', { body }), exportMessagesCsv: (params: ChatMessageQuery) => csvDownload('/admin/chat/messages/export', params), attachments: (params: ChatAttachmentQuery) => request>('GET', '/admin/chat/attachments', { params }), removeAttachment: (id: string) => request('DELETE', `/admin/chat/attachments/${id}`), } 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`), failed: (name: string, params: { limit?: number; offset?: number }) => request('GET', `/admin/queues/${name}/failed`, { params }), } 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}`), stats: () => request('GET', '/admin/tasks/stats'), bulk: (source: string, body: { action: string; ids: string[] }) => request('POST', `/admin/tasks/${source}/bulk`, { body }), } export interface AuditQuery extends Record { q?: string action?: string target_type?: string target_id?: string admin_id?: string admin_username?: string from?: string to?: string limit?: number offset?: number } export const adminLogs = { audit: (params: AuditQuery) => request('GET', '/admin/logs/audit', { params }), jobs: (params: { source?: string; limit?: number; offset?: number }) => request('GET', '/admin/logs/jobs', { params }), exportAuditCsv: (params: AuditQuery) => csvDownload('/admin/logs/audit/export', params), } export const adminAct = { summary: () => request('GET', '/admin/act/summary'), runs: (params: { q?: string state?: string repo_id?: string limit?: number offset?: number }) => request('GET', '/admin/act/runs', { params }), cancelRun: (id: string) => request('POST', `/admin/act/runs/${id}/cancel`), rerunRun: (id: string) => request('POST', `/admin/act/runs/${id}/rerun`), } export const adminRepos = { list: (params: { q?: string space_id?: string archived?: boolean private?: boolean limit?: number offset?: number }) => request('GET', '/admin/repos', { params }), get: (id: string) => request('GET', `/admin/repos/${id}`), update: (id: string, body: UpdateAdminRepoCmd) => request('PATCH', `/admin/repos/${id}`, { body }), } export const adminSystem = { status: () => request('GET', '/admin/system/status'), dashboard: () => request('GET', '/admin/dashboard'), config: () => request('GET', '/admin/system/config'), } export const adminRunners = { list: (params: { limit?: number; offset?: number }) => request('GET', '/admin/runners', { params }), update: (id: string, body: { labels?: string[]; capacity?: number; enabled?: boolean }) => request('PATCH', `/admin/runners/${id}`, { body }), remove: (id: string) => request('DELETE', `/admin/runners/${id}`), tokens: () => request('GET', '/admin/runner-tokens'), createToken: (body: { name: string; space_id?: string | null; expires_in_days?: number }) => request('POST', '/admin/runner-tokens', { body }), revokeToken: (id: string) => request('POST', `/admin/runner-tokens/${id}/revoke`), environments: (spaceId: string) => request('GET', `/admin/spaces/${spaceId}/environments`), upsertEnvironment: ( spaceId: string, body: { name: string; description?: string | null; required_labels?: string[] }, ) => request('PUT', `/admin/spaces/${spaceId}/environments`, { body }), removeEnvironment: (id: string) => request('DELETE', `/admin/environments/${id}`), upsertEnvironmentSecret: (id: string, body: { name: string; value: string }) => request('PUT', `/admin/environments/${id}/secrets`, { body }), removeEnvironmentSecret: (id: string, name: string) => request( 'DELETE', `/admin/environments/${id}/secrets/${encodeURIComponent(name)}`, ), }