b200b73
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<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 function csvDownload(path: string, params?: Record<string, unknown>): 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<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 },
}),
revokeSessions: (id: string) => request<void>('POST', `/admin/admins/${id}/revoke-sessions`),
}
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}`),
create: (body: {
username: string
email: string
password: string
display_name?: string
is_active?: boolean
email_activated?: boolean
}) => request<CreateUserResp>('POST', '/admin/users', { body }),
bulk: (body: {
action: string
ids: string[]
reason?: string
suspension_type?: string
ends_at?: string
}) => request<BulkUsersResp>('POST', '/admin/users/bulk', { body }),
sessions: (uid: string, params: { kind?: string; include_revoked?: boolean; limit?: number; offset?: number }) =>
request<AdminUserSessionPage>('GET', `/admin/users/${uid}/sessions`, { params }),
revokeSession: (uid: string, sessionId: string) =>
request<void>('DELETE', `/admin/users/${uid}/sessions/${sessionId}`),
security: (uid: string) => request<AdminUserSecurity>('GET', `/admin/users/${uid}/security`),
clearTwoFactor: (uid: string) => request<void>('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<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
allow_join?: boolean
allow_invite?: boolean
member_must_enable_2fa?: 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}`,
),
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<string, unknown> {
q?: string
space_id?: string
kind?: string
status?: string
visibility?: string
limit?: number
offset?: number
}
export interface ChatMessageQuery extends Record<string, unknown> {
room_id?: string
author_id?: string
status?: string
q?: string
from?: string
to?: string
limit?: number
offset?: number
}
export interface ChatAttachmentQuery extends Record<string, unknown> {
room_id?: string
user_id?: string
status?: string
q?: string
limit?: number
offset?: number
}
export const adminChat = {
stats: () => request<AdminChatStats>('GET', '/admin/chat/stats'),
rooms: (params: ChatRoomQuery) =>
request<Page<AdminChatRoomSummary>>('GET', '/admin/chat/rooms', { params }),
room: (id: string) => request<AdminChatRoomDetail>('GET', `/admin/chat/rooms/${id}`),
updateRoom: (
id: string,
body: {
name?: string
topic?: string
is_private?: boolean
nsfw?: boolean
is_active?: boolean
},
) => request<AdminChatRoomDetail>('PATCH', `/admin/chat/rooms/${id}`, { body }),
removeRoom: (id: string) =>
request<DeleteChatRoomResp>('DELETE', `/admin/chat/rooms/${id}`),
members: (id: string, params: { status?: string; limit?: number; offset?: number }) =>
request<Page<AdminChatRoomMember>>('GET', `/admin/chat/rooms/${id}/members`, { params }),
removeMember: (id: string, uid: string) =>
request<RemoveChatMemberResp>('DELETE', `/admin/chat/rooms/${id}/members/${uid}`),
purgeRoom: (id: string) =>
request<PurgeChatMessagesResp>('POST', `/admin/chat/rooms/${id}/purge`),
messages: (params: ChatMessageQuery) =>
request<Page<AdminChatMessage>>('GET', '/admin/chat/messages', { params }),
message: (id: string) =>
request<AdminChatMessageDetail>('GET', `/admin/chat/messages/${id}`),
bulkDelete: (body: { message_ids: string[]; reason?: string }) =>
request<BulkChatMessageResp>('POST', '/admin/chat/messages/bulk-delete', { body }),
exportMessagesCsv: (params: ChatMessageQuery) =>
csvDownload('/admin/chat/messages/export', params),
attachments: (params: ChatAttachmentQuery) =>
request<Page<AdminChatAttachment>>('GET', '/admin/chat/attachments', { params }),
removeAttachment: (id: string) =>
request<DeleteChatAttachmentResp>('DELETE', `/admin/chat/attachments/${id}`),
}
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`),
failed: (name: string, params: { limit?: number; offset?: number }) =>
request<JobLogPage>('GET', `/admin/queues/${name}/failed`, { params }),
}
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}`),
stats: () => request<TaskStats>('GET', '/admin/tasks/stats'),
bulk: (source: string, body: { action: string; ids: string[] }) =>
request<BulkTaskResp>('POST', `/admin/tasks/${source}/bulk`, { body }),
}
export interface AuditQuery extends Record<string, unknown> {
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<AuditLogPage>('GET', '/admin/logs/audit', { params }),
jobs: (params: { source?: string; limit?: number; offset?: number }) =>
request<JobLogPage>('GET', '/admin/logs/jobs', { params }),
exportAuditCsv: (params: AuditQuery) => csvDownload('/admin/logs/audit/export', params),
}
export const adminAct = {
summary: () => request<AdminActSummary>('GET', '/admin/act/summary'),
runs: (params: {
q?: string
state?: string
repo_id?: string
limit?: number
offset?: number
}) => request<AdminActRunPage>('GET', '/admin/act/runs', { params }),
cancelRun: (id: string) => request<string>('POST', `/admin/act/runs/${id}/cancel`),
rerunRun: (id: string) => request<string>('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<AdminRepoPage>('GET', '/admin/repos', { params }),
get: (id: string) => request<AdminRepoSummary>('GET', `/admin/repos/${id}`),
update: (id: string, body: UpdateAdminRepoCmd) =>
request<AdminRepoSummary>('PATCH', `/admin/repos/${id}`, { body }),
}
export const adminSystem = {
status: () => request<SystemStatus>('GET', '/admin/system/status'),
dashboard: () => request<Dashboard>('GET', '/admin/dashboard'),
config: () => request<SystemConfig>('GET', '/admin/system/config'),
}
export const adminRunners = {
list: (params: { limit?: number; offset?: number }) =>
request<RunnerPage>('GET', '/admin/runners', { params }),
update: (id: string, body: { labels?: string[]; capacity?: number; enabled?: boolean }) =>
request<RunnerSummary>('PATCH', `/admin/runners/${id}`, { body }),
remove: (id: string) => request<RunnerSummary[]>('DELETE', `/admin/runners/${id}`),
tokens: () => request<RunnerTokenSummary[]>('GET', '/admin/runner-tokens'),
createToken: (body: { name: string; space_id?: string | null; expires_in_days?: number }) =>
request<CreatedRunnerToken>('POST', '/admin/runner-tokens', { body }),
revokeToken: (id: string) =>
request<RunnerTokenSummary[]>('POST', `/admin/runner-tokens/${id}/revoke`),
environments: (spaceId: string) =>
request<EnvironmentAdminSummary[]>('GET', `/admin/spaces/${spaceId}/environments`),
upsertEnvironment: (
spaceId: string,
body: { name: string; description?: string | null; required_labels?: string[] },
) => request<EnvironmentAdminSummary>('PUT', `/admin/spaces/${spaceId}/environments`, { body }),
removeEnvironment: (id: string) =>
request<EnvironmentAdminSummary[]>('DELETE', `/admin/environments/${id}`),
upsertEnvironmentSecret: (id: string, body: { name: string; value: string }) =>
request<EnvironmentAdminSummary[]>('PUT', `/admin/environments/${id}/secrets`, { body }),
removeEnvironmentSecret: (id: string, name: string) =>
request<EnvironmentAdminSummary[]>(
'DELETE',
`/admin/environments/${id}/secrets/${encodeURIComponent(name)}`,
),
}