Skip to content
Clone
98e2c80
226 lines 7.3 KB
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<T> {
code: number
message: string
data: T | null
error: string | null
error_code: string | null
}
function qs(params?: Record<string, unknown>): 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<T>(
method: string,
path: string,
options?: { params?: Record<string, unknown>; body?: unknown },
): Promise<T> {
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<T> | null = null
if (text) {
try {
envelope = JSON.parse(text) as Envelope<T>
} 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<AdminLoginResp>('POST', '/admin/auth/login', { body: { username, password } }),
logout: () => request<void>('POST', '/admin/auth/logout'),
me: () => request<AdminDto>('GET', '/admin/me'),
changePassword: (currentPassword: string, newPassword: string) =>
request<void>('PATCH', '/admin/me/password', {
body: { current_password: currentPassword, new_password: newPassword },
}),
}
export const adminAccounts = {
list: (params: { limit?: number; offset?: number }) =>
request<Page<AdminDto>>('GET', '/admin/admins', { params }),
create: (body: {
username: string
display_name?: string
password: string
role: string
}) => request<AdminDto>('POST', '/admin/admins', { body }),
update: (
id: string,
body: { display_name?: string; role?: string; is_active?: boolean },
) => request<AdminDto>('PATCH', `/admin/admins/${id}`, { body }),
remove: (id: string) => request<void>('DELETE', `/admin/admins/${id}`),
resetPassword: (id: string, newPassword: string) =>
request<void>('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<Page<AdminUserSummary>>('GET', '/admin/users', { params }),
get: (uid: string) => request<AdminUserDetail>('GET', `/admin/users/${uid}`),
update: (
uid: string,
body: {
display_name?: string
is_active?: boolean
is_restricted?: boolean
prohibit_login?: boolean
},
) => request<AdminUserSummary>('PATCH', `/admin/users/${uid}`, { body }),
suspend: (
uid: string,
body: { suspension_type: string; reason?: string; ends_at?: string },
) => request<AdminSuspension>('POST', `/admin/users/${uid}/suspend`, { body }),
revokeSuspension: (uid: string, suspensionId: string) =>
request<void>('POST', `/admin/users/${uid}/suspensions/${suspensionId}/revoke`),
resetPassword: (uid: string, newPassword: string) =>
request<void>('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<Page<AdminSpaceSummary>>('GET', '/admin/spaces', { params }),
get: (id: string) => request<AdminSpaceDetail>('GET', `/admin/spaces/${id}`),
update: (
id: string,
body: { name?: string; description?: string | null; is_private?: boolean; is_active?: boolean },
) => request<AdminSpaceDetail>('PATCH', `/admin/spaces/${id}`, { body }),
transfer: (id: string, newOwnerId: string) =>
request<AdminSpaceDetail>('POST', `/admin/spaces/${id}/transfer`, {
body: { new_owner_id: newOwnerId },
}),
remove: (id: string) => request<void>('DELETE', `/admin/spaces/${id}`),
members: (id: string, params: { status?: string; limit?: number; offset?: number }) =>
request<Page<AdminSpaceMember>>('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<StorageOverview>('GET', '/admin/storage/overview', { params: { refresh } }),
objects: (params: { prefix?: string; token?: string; limit?: number }) =>
request<StorageObjectPage>('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<QueueSnapshot[]>('GET', '/admin/queues'),
requeueFailed: (name: string) =>
request<QueueActionResp>('POST', `/admin/queues/${name}/requeue-failed`),
purge: (name: string) => request<QueueActionResp>('POST', `/admin/queues/${name}/purge`),
}
export const adminTasks = {
list: (params: {
source?: string
state?: string
q?: string
limit?: number
offset?: number
}) => request<TaskPage>('GET', '/admin/tasks', { params }),
get: (source: string, id: string) => request<TaskDetail>('GET', `/admin/tasks/${source}/${id}`),
retry: (source: string, id: string) =>
request<TaskActionResp>('POST', `/admin/tasks/${source}/${id}/retry`),
cancel: (source: string, id: string) =>
request<TaskActionResp>('POST', `/admin/tasks/${source}/${id}/cancel`),
remove: (source: string, id: string) => request<void>('DELETE', `/admin/tasks/${source}/${id}`),
}
export const adminLogs = {
audit: (params: {
action?: string
target_type?: string
admin_id?: string
limit?: number
offset?: number
}) => request<AuditLogPage>('GET', '/admin/logs/audit', { params }),
jobs: (params: { source?: string; limit?: number; offset?: number }) =>
request<JobLogPage>('GET', '/admin/logs/jobs', { params }),
}
export const adminSystem = {
status: () => request<SystemStatus>('GET', '/admin/system/status'),
}