import axios from 'axios'; interface MCPPluginSimpleCreate { config_json: string; enabled: boolean; } import { message } from 'antd'; import { ssePost } from '../utils/sseClient'; import type { SSEClientOptions } from '../utils/sseClient'; import type { User, AuthUrlResponse, Project, ProjectCreate, ProjectUpdate, WorldBuildingResponse, Outline, OutlineCreate, OutlineUpdate, OutlineReorderRequest, Character, CharacterUpdate, Chapter, ChapterCreate, ChapterUpdate, GenerateOutlineRequest, GenerateCharacterRequest, PolishTextRequest, GenerateCharactersResponse, GenerateOutlineResponse, Settings, SettingsUpdate, WritingStyle, WritingStyleCreate, WritingStyleUpdate, PresetStyle, WritingStyleListResponse, MCPPlugin, MCPPluginCreate, MCPPluginUpdate, MCPTestResult, MCPTool, MCPToolCallRequest, MCPToolCallResponse, } from '../types'; const api = axios.create({ baseURL: '/api', timeout: 120000, headers: { 'Content-Type': 'application/json', }, withCredentials: true, }); api.interceptors.request.use( (config) => { return config; }, (error) => { return Promise.reject(error); } ); api.interceptors.response.use( (response) => { return response.data; }, (error) => { let errorMessage = '请求失败'; if (error.response) { const status = error.response.status; const data = error.response.data; switch (status) { case 400: errorMessage = data?.detail || '请求参数错误'; break; case 401: errorMessage = '未授权,请先登录'; if (window.location.pathname !== '/login') { window.location.href = '/login'; } break; case 403: errorMessage = '没有权限访问'; break; case 404: errorMessage = data?.detail || '请求的资源不存在'; break; case 422: errorMessage = data?.detail || '请求参数验证失败'; if (data?.errors) { console.error('验证错误详情:', data.errors); } break; case 500: errorMessage = data?.detail || '服务器内部错误'; break; case 503: errorMessage = '服务暂时不可用,请稍后重试'; break; default: errorMessage = data?.detail || data?.message || `请求失败 (${status})`; } } else if (error.request) { errorMessage = '网络错误,请检查网络连接'; } else { errorMessage = error.message || '请求失败'; } message.error(errorMessage); console.error('API Error:', errorMessage, error); return Promise.reject(error); } ); export const authApi = { getAuthConfig: () => api.get('/auth/config'), localLogin: (username: string, password: string) => api.post('/auth/local/login', { username, password }), bindAccountLogin: (username: string, password: string) => api.post('/auth/bind/login', { username, password }), getLinuxDOAuthUrl: () => api.get('/auth/linuxdo/url'), getCurrentUser: () => api.get('/auth/user'), getPasswordStatus: () => api.get('/auth/password/status'), setPassword: (password: string) => api.post('/auth/password/set', { password }), refreshSession: () => api.post('/auth/refresh'), logout: () => api.post('/auth/logout'), }; export const userApi = { getCurrentUser: () => api.get('/users/current'), listUsers: () => api.get('/users'), setAdmin: (userId: string, isAdmin: boolean) => api.post('/users/set-admin', { user_id: userId, is_admin: isAdmin }), deleteUser: (userId: string) => api.delete(`/users/${userId}`), getUser: (userId: string) => api.get(`/users/${userId}`), resetPassword: (userId: string, newPassword?: string) => api.post('/users/reset-password', { user_id: userId, new_password: newPassword }), }; export const settingsApi = { getSettings: () => api.get('/settings'), saveSettings: (data: SettingsUpdate) => api.post('/settings', data), updateSettings: (data: SettingsUpdate) => api.put('/settings', data), deleteSettings: () => api.delete('/settings'), getAvailableModels: (params: { api_key: string; api_base_url: string; provider: string }) => api.get; count?: number }>('/settings/models', { params }), testApiConnection: (params: { api_key: string; api_base_url: string; provider: string; llm_model: string }) => api.post; error?: string; error_type?: string; suggestions?: string[]; }>('/settings/test', params), }; export const projectApi = { getProjects: () => api.get('/projects'), getProject: (id: string) => api.get(`/projects/${id}`), createProject: (data: ProjectCreate) => api.post('/projects', data), updateProject: (id: string, data: ProjectUpdate) => api.put(`/projects/${id}`, data), deleteProject: (id: string) => api.delete(`/projects/${id}`), exportProject: (id: string) => { window.open(`/api/projects/${id}/export`, '_blank'); }, // 导出项目数据为JSON exportProjectData: async (id: string, options: { include_generation_history?: boolean; include_writing_styles?: boolean }) => { const response = await axios.post( `/api/projects/${id}/export-data`, options, { responseType: 'blob', headers: { 'Content-Type': 'application/json', }, } ); // 从响应头获取文件名 const contentDisposition = response.headers['content-disposition']; let filename = 'project_export.json'; if (contentDisposition) { const matches = /filename\*=UTF-8''(.+)/.exec(contentDisposition); if (matches && matches[1]) { filename = decodeURIComponent(matches[1]); } } // 创建下载链接 const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', filename); document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); }, // 验证导入文件 validateImportFile: (file: File) => { const formData = new FormData(); formData.append('file', file); return api.post; errors: string[]; warnings: string[]; }>('/projects/validate-import', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); }, // 导入项目 importProject: (file: File) => { const formData = new FormData(); formData.append('file', file); return api.post; warnings: string[]; }>('/projects/import', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); }, }; export const outlineApi = { getOutlines: (projectId: string) => api.get(`/outlines/project/${projectId}`).then(res => res.items), getOutline: (id: string) => api.get(`/outlines/${id}`), createOutline: (data: OutlineCreate) => api.post('/outlines', data), updateOutline: (id: string, data: OutlineUpdate) => api.put(`/outlines/${id}`, data), deleteOutline: (id: string) => api.delete(`/outlines/${id}`), reorderOutlines: (data: OutlineReorderRequest) => api.post('/outlines/reorder', data), generateOutline: (data: GenerateOutlineRequest) => api.post('/outlines/generate', data).then(res => res.items), }; export const characterApi = { getCharacters: (projectId: string) => api.get(`/characters/project/${projectId}`), getCharacter: (id: string) => api.get(`/characters/${id}`), updateCharacter: (id: string, data: CharacterUpdate) => api.put(`/characters/${id}`, data), deleteCharacter: (id: string) => api.delete(`/characters/${id}`), generateCharacter: (data: GenerateCharacterRequest) => api.post('/characters/generate', data), }; export const chapterApi = { getChapters: (projectId: string) => api.get(`/chapters/project/${projectId}`), getChapter: (id: string) => api.get(`/chapters/${id}`), createChapter: (data: ChapterCreate) => api.post('/chapters', data), updateChapter: (id: string, data: ChapterUpdate) => api.put(`/chapters/${id}`, data), deleteChapter: (id: string) => api.delete(`/chapters/${id}`), checkCanGenerate: (chapterId: string) => api.get(`/chapters/${chapterId}/can-generate`), // 章节重新生成相关 getRegenerationTasks: (chapterId: string, limit?: number) => api.get; }>(`/chapters/${chapterId}/regeneration/tasks`, { params: { limit } }), }; export const writingStyleApi = { // 获取预设风格列表 getPresetStyles: () => api.get('/writing-styles/presets/list'), // 获取项目的所有风格 getProjectStyles: (projectId: string) => api.get(`/writing-styles/project/${projectId}`), // 创建新风格(基于预设或自定义) createStyle: (data: WritingStyleCreate) => api.post('/writing-styles', data), // 更新风格 updateStyle: (styleId: number, data: WritingStyleUpdate) => api.put(`/writing-styles/${styleId}`, data), // 删除风格 deleteStyle: (styleId: number) => api.delete(`/writing-styles/${styleId}`), // 设置默认风格 setDefaultStyle: (styleId: number, projectId: string) => api.post(`/writing-styles/${styleId}/set-default`, { project_id: projectId }), // 为项目初始化默认风格(如果没有任何风格) initializeDefaultStyles: (projectId: string) => api.post(`/writing-styles/project/${projectId}/initialize`, {}), }; export const polishApi = { polishText: (data: PolishTextRequest) => api.post('/polish', data), polishBatch: (texts: string[]) => api.post('/polish/batch', { texts }), }; export default api; export const wizardStreamApi = { generateWorldBuildingStream: ( data: { title: string; description: string; theme: string; genre: string | string[]; narrative_perspective?: string; target_words?: number; chapter_count?: number; character_count?: number; provider?: string; model?: string; }, options?: SSEClientOptions ) => ssePost( '/api/wizard-stream/world-building', data, options ), generateCharactersStream: ( data: { project_id: string; count?: number; world_context?: Record; theme?: string; genre?: string; requirements?: string; provider?: string; model?: string; }, options?: SSEClientOptions ) => ssePost( '/api/wizard-stream/characters', data, options ), generateCompleteOutlineStream: ( data: { project_id: string; chapter_count: number; narrative_perspective: string; target_words?: number; requirements?: string; provider?: string; model?: string; }, options?: SSEClientOptions ) => ssePost( '/api/wizard-stream/outline', data, options ), updateWorldBuildingStream: ( projectId: string, data: { time_period?: string; location?: string; atmosphere?: string; rules?: string; }, options?: SSEClientOptions ) => ssePost( `/api/wizard-stream/world-building/${projectId}`, data, options ), regenerateWorldBuildingStream: ( projectId: string, data?: { provider?: string; model?: string; }, options?: SSEClientOptions ) => ssePost( `/api/wizard-stream/world-building/${projectId}/regenerate`, data || {}, options ), cleanupWizardDataStream: ( projectId: string, options?: SSEClientOptions ) => ssePost<{ message: string; deleted: { characters: number; outlines: number; chapters: number } }>( `/api/wizard-stream/cleanup/${projectId}`, {}, options ), }; export const mcpPluginApi = { // 获取所有插件 getPlugins: () => api.get('/mcp/plugins'), // 获取单个插件 getPlugin: (id: string) => api.get(`/mcp/plugins/${id}`), // 创建插件 createPlugin: (data: MCPPluginCreate) => api.post('/mcp/plugins', data), // 简化创建插件(通过标准MCP配置JSON) createPluginSimple: (data: MCPPluginSimpleCreate) => api.post('/mcp/plugins/simple', data), // 更新插件 updatePlugin: (id: string, data: MCPPluginUpdate) => api.put(`/mcp/plugins/${id}`, data), // 删除插件 deletePlugin: (id: string) => api.delete(`/mcp/plugins/${id}`), // 启用/禁用插件 togglePlugin: (id: string, enabled: boolean) => api.post(`/mcp/plugins/${id}/toggle`, null, { params: { enabled } }), // 测试插件连接 testPlugin: (id: string) => api.post(`/mcp/plugins/${id}/test`), // 获取插件工具列表 getPluginTools: (id: string) => api.get(`/mcp/plugins/${id}/tools`), // 调用工具 callTool: (data: MCPToolCallRequest) => api.post('/mcp/call', data), }; // 管理员API export const adminApi = { // 获取用户列表 getUsers: () => api.get('/admin/users'), // 添加用户 createUser: (data: { username: string; display_name: string; password?: string; avatar_url?: string; trust_level?: number; is_admin?: boolean; }) => api.post('/admin/users', data), // 编辑用户 updateUser: (userId: string, data: { display_name?: string; avatar_url?: string; trust_level?: number; }) => api.put(`/admin/users/${userId}`, data), // 切换用户状态(启用/禁用) toggleUserStatus: (userId: string, isActive: boolean) => api.post(`/admin/users/${userId}/toggle-status`, { is_active: isActive }), // 重置密码 resetPassword: (userId: string, newPassword?: string) => api.post(`/admin/users/${userId}/reset-password`, { new_password: newPassword }), // 删除用户 deleteUser: (userId: string) => api.delete(`/admin/users/${userId}`), };