/** * API 服務層 - 處理所有後端 API 呼叫 */ const API_BASE_URL = '/api/v1'; class ApiService { constructor() { this.baseUrl = API_BASE_URL; } /** * 取得認證 Token */ getToken() { return localStorage.getItem('token'); } /** * 設定認證 Token */ setToken(token) { localStorage.setItem('token', token); } /** * 清除認證 Token */ clearToken() { localStorage.removeItem('token'); localStorage.removeItem('user'); } /** * 建立請求標頭 */ getHeaders(includeAuth = true) { const headers = { 'Content-Type': 'application/json' }; if (includeAuth) { const token = this.getToken(); if (token) { headers['Authorization'] = `Bearer ${token}`; } } return headers; } /** * 發送 API 請求 */ async request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; const config = { headers: this.getHeaders(options.auth !== false), ...options }; try { const response = await fetch(url, config); // 處理 401 未授權 if (response.status === 401) { this.clearToken(); window.location.reload(); throw new Error('登入已過期,請重新登入'); } // 處理 204 無內容 if (response.status === 204) { return { success: true }; } const data = await response.json(); if (!response.ok) { throw new Error(data.detail || '請求失敗'); } return data; } catch (error) { console.error('API Error:', error); throw error; } } /** * GET 請求 */ async get(endpoint, params = {}) { const queryString = new URLSearchParams(params).toString(); const url = queryString ? `${endpoint}?${queryString}` : endpoint; return this.request(url, { method: 'GET' }); } /** * POST 請求 */ async post(endpoint, data = {}, options = {}) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data), ...options }); } /** * PUT 請求 */ async put(endpoint, data = {}) { return this.request(endpoint, { method: 'PUT', body: JSON.stringify(data) }); } /** * DELETE 請求 */ async delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); } /** * 上傳檔案 */ async upload(endpoint, formData) { const token = this.getToken(); const headers = {}; if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(`${this.baseUrl}${endpoint}`, { method: 'POST', headers, body: formData }); if (!response.ok) { const data = await response.json(); throw new Error(data.detail || '上傳失敗'); } return response.json(); } } // 建立全域 API 實例 (必須在其他 API 模組之前) const api = new ApiService(); // ============ 認證 API ============ const authApi = { /** * 登入 */ async login(username, password, authType = 'local') { const response = await api.post('/auth/login', { username, password, auth_type: authType }, { auth: false }); if (response.token) { api.setToken(response.token); localStorage.setItem('user', JSON.stringify(response.user)); } return response; }, /** * 登出 */ async logout() { try { await api.post('/auth/logout'); } finally { api.clearToken(); } }, /** * 取得當前用戶 */ async getMe() { return api.get('/auth/me'); }, /** * 檢查是否已登入 */ isLoggedIn() { return !!api.getToken(); }, /** * 取得本地儲存的用戶資訊 */ getUser() { const user = localStorage.getItem('user'); return user ? JSON.parse(user) : null; } }; // ============ 用戶管理 API ============ const usersApi = { /** * 取得用戶列表 */ async getList(params = {}) { return api.get('/users', params); }, /** * 取得單一用戶 */ async getById(userId) { return api.get(`/users/${userId}`); }, /** * 建立用戶 */ async create(userData) { return api.post('/users', userData); }, /** * 更新用戶 */ async update(userId, userData) { return api.put(`/users/${userId}`, userData); }, /** * 刪除用戶 */ async delete(userId) { return api.delete(`/users/${userId}`); } }; // ============ 群組管理 API ============ const groupsApi = { /** * 取得群組列表 */ async getList(params = {}) { return api.get('/groups', params); }, /** * 取得群組詳情 */ async getById(groupId) { return api.get(`/groups/${groupId}`); }, /** * 建立群組 */ async create(groupData) { return api.post('/groups', groupData); }, /** * 更新群組 */ async update(groupId, groupData) { return api.put(`/groups/${groupId}`, groupData); }, /** * 刪除群組 */ async delete(groupId) { return api.delete(`/groups/${groupId}`); }, /** * 取得群組關鍵字 */ async getKeywords(groupId) { return api.get(`/groups/${groupId}/keywords`); }, /** * 新增關鍵字 */ async addKeyword(groupId, keyword) { return api.post(`/groups/${groupId}/keywords`, { keyword }); }, /** * 刪除關鍵字 */ async deleteKeyword(groupId, keywordId) { return api.delete(`/groups/${groupId}/keywords/${keywordId}`); } }; // ============ 訂閱管理 API ============ const subscriptionsApi = { /** * 取得我的訂閱 */ async getMySubscriptions() { return api.get('/subscriptions'); }, /** * 更新訂閱 */ async update(subscriptions) { return api.put('/subscriptions', { subscriptions }); } }; // ============ 報告管理 API ============ const reportsApi = { /** * 取得報告列表 */ async getList(params = {}) { return api.get('/reports', params); }, /** * 取得今日報告 */ async getToday() { return api.get('/reports/today'); }, /** * 取得報告詳情 */ async getById(reportId) { return api.get(`/reports/${reportId}`); }, /** * 更新報告 */ async update(reportId, reportData) { return api.put(`/reports/${reportId}`, reportData); }, /** * 發布報告 */ async publish(reportId) { return api.post(`/reports/${reportId}/publish`); }, /** * 重新產生 AI 摘要 */ async regenerateSummary(reportId) { return api.post(`/reports/${reportId}/regenerate-summary`); }, /** * 匯出報告 PDF */ async exportPdf(reportId) { const token = api.getToken(); const response = await fetch(`${API_BASE_URL}/reports/${reportId}/export`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) { throw new Error('匯出失敗'); } return response.blob(); } }; // ============ 系統設定 API ============ const settingsApi = { /** * 取得系統設定 */ async get() { return api.get('/settings'); }, /** * 更新系統設定 */ async update(settings) { return api.put('/settings', settings); }, /** * 測試 LLM 連線 */ async testLlm() { return api.post('/settings/llm/test'); }, /** * 上傳 PDF Logo */ async uploadLogo(file) { const formData = new FormData(); formData.append('logo', file); return api.upload('/settings/pdf/logo', formData); }, /** * 取得管理員儀表板數據 */ async getAdminDashboard() { return api.get('/settings/dashboard/admin'); } }; // 匯出所有 API 到 window window.api = api; window.authApi = authApi; window.usersApi = usersApi; window.groupsApi = groupsApi; window.subscriptionsApi = subscriptionsApi; window.reportsApi = reportsApi; window.settingsApi = settingsApi;