import {BaseService} from '@/lib/services'; import type { AdminUser, AuthSource, AuthSourceRequest, CacheConfig, CacheStatus, CreateScheduleRequest, CreateSystemConfigRequest, CreateTemplateRequest, CreateUserRequest, DatabaseInfo, DispatchTaskRequest, ListTaskExecutionsRequest, ListTaskExecutionsResponse, ListUsersRequest, ListUsersResponse, Schedule, SystemConfig, SystemStatus, TaskExecution, TaskMeta, TaskParamType, TaskTypeResponse, Template, ToggleAuthSourceRequest, UpdateScheduleRequest, UpdateSystemConfigRequest, UpdateTemplateRequest, UpdateUserStatusRequest, } from './types'; export type { AdminUser, CacheStatus, CacheConfig } from './types'; /** * 管理员服务 * * @remarks * 所有接口都需要管理员权限 */ export class AdminService extends BaseService { protected static readonly basePath = '/api/v1/admin'; // ==================== 系统配置管理 ==================== /** * 创建系统配置 * @param request - 创建系统配置的请求参数 * @returns void * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 * @throws {ValidationError} 当参数验证失败或配置键已存在时 * * @example * ```typescript * await AdminService.createSystemConfig({ * key: 'app.version', * value: '1.0.0', * description: '应用版本号' * }); * ``` */ static async createSystemConfig( request: CreateSystemConfigRequest, ): Promise { return this.post('/system-configs', request); } /** * 获取系统配置列表 * @returns 系统配置列表 * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 * * @example * ```typescript * const configs = await AdminService.listSystemConfigs(); * console.log('系统配置数量:', configs.length); * ``` */ static async listSystemConfigs(type?: 'system' | 'business'): Promise { const query = type ? `?type=${type}` : ''; return this.get(`/system-configs${query}`); } /** * 获取单个系统配置 * @param key - 配置键 * @returns 系统配置信息 * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 * @throws {NotFoundError} 当配置不存在时 * * @example * ```typescript * const config = await AdminService.getSystemConfig('app.version'); * console.log('应用版本:', config.value); * ``` */ static async getSystemConfig(key: string): Promise { return this.get(`/system-configs/${ key }`); } /** * 更新系统配置 * @param key - 配置键 * @param request - 更新系统配置的请求参数 * @returns void * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 * @throws {NotFoundError} 当配置不存在时 * @throws {ValidationError} 当参数验证失败时 * * @example * ```typescript * await AdminService.updateSystemConfig('app.version', { * value: '1.1.0', * description: '更新到新版本' * }); * ``` */ static async updateSystemConfig( key: string, request: UpdateSystemConfigRequest, ): Promise { return this.put(`/system-configs/${ key }`, request); } static async testSMTP(request: { smtp_host: string; smtp_port: number; smtp_username: string; smtp_password: string; to: string; }): Promise<{ success: boolean; log: string; error: string }> { return this.post<{ success: boolean; log: string; error: string }>('/system-configs/smtp/test', request); } /** * 获取所有已存在的文件业务类型及默认内置类型列表 * @returns 业务类型列表 */ static async listUploadTypes(): Promise { return this.get('/uploads/types'); } // ==================== 认证源管理 ==================== static async listAuthSources(): Promise { return this.get('/auth-sources'); } static async createAuthSource(request: AuthSourceRequest): Promise { return this.post('/auth-sources', request); } static async updateAuthSource(id: string, request: AuthSourceRequest): Promise { return this.put(`/auth-sources/${ id }`, request); } static async toggleAuthSource(id: string, request: ToggleAuthSourceRequest): Promise { return this.put(`/auth-sources/${ id }/toggle`, request); } static async deleteAuthSource(id: string): Promise { return this.delete(`/auth-sources/${ id }`); } // ==================== 任务管理 ==================== /** * 获取支持的任务类型列表 * @returns 任务类型列表 * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 * * @example * ```typescript * const taskTypes = await AdminService.getTaskTypes(); * console.log('可用任务类型:', taskTypes); * ``` */ static async getTaskTypes(): Promise { const response = await this.get('/tasks/types'); // Adapt backend PascalCase to frontend snake_case return response.map(item => ({ type: item.Type || item.type || '', asynq_task: item.AsynqTask || item.asynq_task || '', name: item.Name || item.name || '', description: item.Description || item.description || '', supports_time: item.SupportsTime ?? item.supports_time ?? false, max_retry: item.MaxRetry ?? item.max_retry ?? 0, queue: item.Queue || item.queue || '', params: (item.Params || item.params || []).map(p => ({ name: p.Name || p.name || '', label: p.Label || p.label || '', // Cast to TaskParamType: backend only emits 'string' | 'text' | 'number'. // Unknown future values fall through safely to the string branch. type: (p.Type || p.type || 'string') as TaskParamType, required: p.Required ?? p.required ?? false, placeholder: p.Placeholder || p.placeholder || '', description: p.Description || p.description || '', })), })); } /** * 下发任务 * @param request - 下发任务请求参数 * @returns void * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 * @throws {ValidationError} 当参数验证失败时 * @remarks * - 不同任务类型需要不同的参数 * - order_sync 支持 start_time 和 end_time 参数 * - user_gamification 需要 user_id 参数 * - 其他任务无需额外参数 */ static async dispatchTask(request: DispatchTaskRequest): Promise { return this.post('/tasks/dispatch', request); } /** * 查询任务执行记录列表 */ static async listTaskExecutions( request: ListTaskExecutionsRequest = {}, ): Promise { return this.get( '/tasks/executions', request as unknown as Record, ); } /** * 查询任务执行详情 */ static async getTaskExecution(id: string): Promise { return this.get(`/tasks/executions/${ id }`); } /** * 重试失败任务 */ static async retryTaskExecution(id: string): Promise { return this.post(`/tasks/executions/${ id }/retry`); } // ==================== 定时任务管理 ==================== /** * 获取定时任务列表 */ static async listSchedules(): Promise { return this.get('/tasks/schedules'); } /** * 创建定时任务 */ static async createSchedule(request: CreateScheduleRequest): Promise { return this.post('/tasks/schedules', request); } /** * 更新定时任务 */ static async updateSchedule(id: string, request: UpdateScheduleRequest): Promise { return this.put(`/tasks/schedules/${ id }`, request); } /** * 删除定时任务 */ static async deleteSchedule(id: string): Promise { return this.delete(`/tasks/schedules/${ id }`); } // ==================== 用户管理 ==================== /** * 获取用户列表 * @param request - 查询参数 * @returns 用户列表及总数 * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 * @throws {ValidationError} 当参数验证失败时 * * @example * ```typescript * const result = await AdminService.listUsers({ * page: 1, * page_size: 20, * user_id: '10001', * username: 'test' * }); * console.log('用户总数:', result.total); * console.log('用户列表:', result.users); * ``` * * @remarks * - page 从 1 开始 * - page_size 范围 1-100 * - user_id 按用户 ID 精确搜索 * - username 按用户名做前缀搜索 */ static async listUsers(request: ListUsersRequest): Promise { return this.get('/users', request as unknown as Record); } /** * 获取用户详情 * @param id - 用户 ID * @returns 用户完整资料 */ static async getUser(id: string): Promise { return this.get(`/users/${ id }`); } /** * 更新用户状态 * @param id - 用户 ID * @param request - 更新状态请求参数 * @returns void * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限或禁用管理员用户时 * @throws {NotFoundError} 当用户不存在时 * * @example * ```typescript * // 禁用用户 * await AdminService.updateUserStatus(123, { is_active: false }); * * // 启用用户 * await AdminService.updateUserStatus(123, { is_active: true }); * ``` * * @remarks * - 不能禁用管理员用户 */ static async updateUserStatus( id: string, request: UpdateUserStatusRequest ): Promise { return this.put(`/users/${ id }/status`, request); } /** * 创建用户 * @param request - 创建用户请求参数 * @returns 创建成功的用户信息 */ static async createUser(request: CreateUserRequest): Promise { return this.post('/users', request); } /** * 删除用户 * @param id - 用户 ID */ static async deleteUser(id: string): Promise { return this.delete(`/users/${ id }`); } /** * 获取系统状态 * @returns 系统状态指标数据 * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 */ static async getSystemStatus(): Promise { return this.get('/status'); } /** * 获取数据库信息 * @returns 数据库类型、名称、版本信息 * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 */ static async getDatabaseInfo(): Promise { return this.get('/db-info'); } /** * 导出数据库 * SQLite 返回 .db 二进制,PostgreSQL 返回 pg_dump .sql 文本 * @returns 包含文件 Blob 和建议文件名的对象 * @throws {UnauthorizedError} 当未登录时 * @throws {ForbiddenError} 当无管理员权限时 */ static async exportDatabase(): Promise<{ blob: Blob; filename: string }> { const response = await import('axios').then(({ default: axios }) => axios.get(this.getFullPath('/db-export'), { withCredentials: true, responseType: 'blob', }) ); // 从 Content-Disposition 提取文件名 const disposition = response.headers['content-disposition'] as string | undefined; let filename = 'wavelet_export'; if (disposition) { const match = disposition.match(/filename="?([^";]+)"?/); if (match) filename = match[1]; } return { blob: response.data, filename }; } // ==================== 系统日志 ==================== /** * 获取系统历史日志 * @param cursor - 日志游标,0=获取最新,>0=获取更早 * @param limit - 每页条数,默认 200 */ static async getLogs(cursor: number = 0, limit: number = 200): Promise<{ lines: Array<{ index: number; data: string }>; has_more: boolean; next_cursor: number; }> { return this.get('/logs', { cursor, limit }); } /** * 获取 ClickHouse 访问日志列表 */ static async getAccessLogs(params: { page: number; page_size: number; username?: string; path?: string; start_time?: string; end_time?: string; }): Promise<{ total: number; list: Array<{ id: string; user_id: string; username: string; nickname: string; path: string; method: string; ip: string; user_agent: string; headers: string; status: number; latency: number; created_at: string; }>; }> { return this.get('/logs/access', params as Record); } /** * 获取 ClickHouse 访问日志图表聚合指标 */ static async getLogsAnalytics(): Promise<{ trend: Array<{ date: string; count: number }>; browsers: Array<{ browser: string; count: number }>; top_users: Array<{ user_id: string; username: string; nickname: string; count: number; }>; }> { return this.get('/logs/analytics'); } // ==================== 模板管理 ==================== /** * 获取模板列表 */ static async listTemplates(): Promise { return this.get('/templates'); } /** * 获取单个模板 */ static async getTemplate(key: string): Promise