- Add 3 user roles: user, admin, super_admin - Restrict LLM config management to super_admin only - Restrict audit logs and statistics to super_admin only - Update AdminPage with role-based tab visibility - Add complete 5 Why prompt from 5why-analyzer.jsx - Add system documentation and authorization guide - Add ErrorModal component and seed test users script 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
225 lines
5.2 KiB
JavaScript
225 lines
5.2 KiB
JavaScript
/**
|
|
* API Client Service
|
|
* 統一的 API 請求處理
|
|
*/
|
|
|
|
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
|
|
|
|
class ApiClient {
|
|
constructor() {
|
|
this.baseURL = API_BASE_URL;
|
|
}
|
|
|
|
async request(endpoint, options = {}) {
|
|
const url = `${this.baseURL}${endpoint}`;
|
|
const config = {
|
|
credentials: 'include', // 重要: 發送 cookies (session)
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
},
|
|
...options,
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(url, config);
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.error || data.message || 'Request failed');
|
|
}
|
|
|
|
return data;
|
|
} catch (error) {
|
|
// 只對非認證錯誤顯示控制台訊息
|
|
if (!endpoint.includes('/auth/me') || !error.message.includes('未登入')) {
|
|
console.error('API Error:', error);
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// GET request
|
|
get(endpoint, options = {}) {
|
|
return this.request(endpoint, {
|
|
method: 'GET',
|
|
...options,
|
|
});
|
|
}
|
|
|
|
// POST request
|
|
post(endpoint, data, options = {}) {
|
|
return this.request(endpoint, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
...options,
|
|
});
|
|
}
|
|
|
|
// PUT request
|
|
put(endpoint, data, options = {}) {
|
|
return this.request(endpoint, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
...options,
|
|
});
|
|
}
|
|
|
|
// DELETE request
|
|
delete(endpoint, options = {}) {
|
|
return this.request(endpoint, {
|
|
method: 'DELETE',
|
|
...options,
|
|
});
|
|
}
|
|
|
|
// ============================================
|
|
// Authentication APIs
|
|
// ============================================
|
|
|
|
async login(identifier, password) {
|
|
return this.post('/api/auth/login', { identifier, password });
|
|
}
|
|
|
|
async logout() {
|
|
return this.post('/api/auth/logout');
|
|
}
|
|
|
|
async getCurrentUser() {
|
|
return this.get('/api/auth/me');
|
|
}
|
|
|
|
async changePassword(oldPassword, newPassword) {
|
|
return this.post('/api/auth/change-password', { oldPassword, newPassword });
|
|
}
|
|
|
|
// ============================================
|
|
// Analysis APIs
|
|
// ============================================
|
|
|
|
async createAnalysis(finding, jobContent, outputLanguage = 'zh-TW') {
|
|
return this.post('/api/analyze', { finding, jobContent, outputLanguage });
|
|
}
|
|
|
|
async translateAnalysis(analysisId, targetLanguage) {
|
|
return this.post('/api/analyze/translate', { analysisId, targetLanguage });
|
|
}
|
|
|
|
async getAnalysisHistory(page = 1, limit = 10, filters = {}) {
|
|
const params = new URLSearchParams({
|
|
page: page.toString(),
|
|
limit: limit.toString(),
|
|
...filters,
|
|
});
|
|
return this.get(`/api/analyze/history?${params}`);
|
|
}
|
|
|
|
async getAnalysisDetail(id) {
|
|
return this.get(`/api/analyze/${id}`);
|
|
}
|
|
|
|
async deleteAnalysis(id) {
|
|
return this.delete(`/api/analyze/${id}`);
|
|
}
|
|
|
|
// ============================================
|
|
// Admin APIs
|
|
// ============================================
|
|
|
|
async getDashboard() {
|
|
return this.get('/api/admin/dashboard');
|
|
}
|
|
|
|
async getUsers(page = 1, limit = 10, filters = {}) {
|
|
const params = new URLSearchParams({
|
|
page: page.toString(),
|
|
limit: limit.toString(),
|
|
...filters,
|
|
});
|
|
return this.get(`/api/admin/users?${params}`);
|
|
}
|
|
|
|
async createUser(userData) {
|
|
return this.post('/api/admin/users', userData);
|
|
}
|
|
|
|
async updateUser(id, userData) {
|
|
return this.put(`/api/admin/users/${id}`, userData);
|
|
}
|
|
|
|
async deleteUser(id) {
|
|
return this.delete(`/api/admin/users/${id}`);
|
|
}
|
|
|
|
async getAllAnalyses(page = 1, limit = 10, filters = {}) {
|
|
const params = new URLSearchParams({
|
|
page: page.toString(),
|
|
limit: limit.toString(),
|
|
...filters,
|
|
});
|
|
return this.get(`/api/admin/analyses?${params}`);
|
|
}
|
|
|
|
async getAuditLogs(page = 1, limit = 10, filters = {}) {
|
|
const params = new URLSearchParams({
|
|
page: page.toString(),
|
|
limit: limit.toString(),
|
|
...filters,
|
|
});
|
|
return this.get(`/api/admin/audit-logs?${params}`);
|
|
}
|
|
|
|
async getStatistics() {
|
|
return this.get('/api/admin/statistics');
|
|
}
|
|
|
|
// ============================================
|
|
// LLM Configuration APIs
|
|
// ============================================
|
|
|
|
async getLLMConfigs() {
|
|
return this.get('/api/llm-config');
|
|
}
|
|
|
|
async getActiveLLMConfig() {
|
|
return this.get('/api/llm-config/active');
|
|
}
|
|
|
|
async createLLMConfig(configData) {
|
|
return this.post('/api/llm-config', configData);
|
|
}
|
|
|
|
async updateLLMConfig(id, configData) {
|
|
return this.put(`/api/llm-config/${id}`, configData);
|
|
}
|
|
|
|
async activateLLMConfig(id) {
|
|
return this.put(`/api/llm-config/${id}/activate`, {});
|
|
}
|
|
|
|
async deleteLLMConfig(id) {
|
|
return this.delete(`/api/llm-config/${id}`);
|
|
}
|
|
|
|
async testLLMConfig(configData) {
|
|
return this.post('/api/llm-config/test', configData);
|
|
}
|
|
|
|
// ============================================
|
|
// Health Check
|
|
// ============================================
|
|
|
|
async healthCheck() {
|
|
return this.get('/health');
|
|
}
|
|
|
|
async dbHealthCheck() {
|
|
return this.get('/health/db');
|
|
}
|
|
}
|
|
|
|
// 建立單例
|
|
const api = new ApiClient();
|
|
|
|
export default api;
|