diff --git a/js/storage/storage-adapter.js b/js/storage/storage-adapter.js index 2a9b0a0..22d9237 100644 --- a/js/storage/storage-adapter.js +++ b/js/storage/storage-adapter.js @@ -71,6 +71,16 @@ class AuthManager { // ---------------- 后端存储实现 ---------------- class BackendStorage { + // 回退到本地存储的方法 + _fallbackTo(method, ...args) { + const localMethod = window[method]; + if (typeof localMethod === 'function') { + return localMethod.apply(null, args); + } + console.warn(`[BackendStorage] No local fallback for ${method}`); + return Promise.resolve(); + } + async fetchAPI(endpoint, options = {}) { const response = await fetch(`${API_BASE_URL}${endpoint}`, { ...options, @@ -94,112 +104,148 @@ class BackendStorage { // 用户设置 async loadSettings() { try { - if (!AuthManager.isAuthenticated()) return this._getDefaultSettings(); + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('loadSettings'); + } const data = await this.fetchAPI('/user/settings'); return data; } catch (error) { console.error('Failed to load settings from backend:', error); - return this._getDefaultSettings(); + return this._fallbackTo('loadSettings'); } } async saveSettings(settings) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('saveSettings', settings); + } await this.fetchAPI('/user/settings', { method: 'PUT', body: JSON.stringify(settings) }); } catch (error) { console.error('Failed to save settings to backend:', error); - throw error; + return this._fallbackTo('saveSettings', settings); } } // API Keys async loadModelKeys(provider) { try { - if (!AuthManager.isAuthenticated()) return []; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('loadModelKeys', provider); + } const keys = await this.fetchAPI(`/user/api-keys?provider=${provider}`); return keys; } catch (error) { console.error('Failed to load API keys:', error); - return []; + return this._fallbackTo('loadModelKeys', provider); } } async saveModelKeys(provider, keys) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('saveModelKeys', provider, keys); + } await this.fetchAPI('/user/api-keys', { method: 'POST', body: JSON.stringify({ provider, keys }) }); } catch (error) { console.error('Failed to save API keys:', error); - throw error; + return this._fallbackTo('saveModelKeys', provider, keys); } } // 文档历史 async saveResultToDB(document) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('saveResultToDB', document); + } await this.fetchAPI('/documents', { method: 'POST', body: JSON.stringify(document) }); } catch (error) { - console.error('Failed to save document:', error); - throw error; + console.error('Failed to save document to backend, falling back to local:', error); + return this._fallbackTo('saveResultToDB', document); } } async getAllResultsFromDB() { try { - if (!AuthManager.isAuthenticated()) return []; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('getAllResultsFromDB'); + } const data = await this.fetchAPI('/documents'); return data.documents || []; } catch (error) { - console.error('Failed to load documents:', error); - return []; + console.error('Failed to load documents from backend, falling back to local:', error); + return this._fallbackTo('getAllResultsFromDB'); } } async getResultFromDB(id) { try { - if (!AuthManager.isAuthenticated()) return null; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('getResultFromDB', id); + } return await this.fetchAPI(`/documents/${id}`); } catch (error) { - console.error('Failed to load document:', error); - return null; + console.error('Failed to load document from backend, falling back to local:', error); + return this._fallbackTo('getResultFromDB', id); } } async deleteResultFromDB(id) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('deleteResultFromDB', id); + } await this.fetchAPI(`/documents/${id}`, { method: 'DELETE' }); } catch (error) { - console.error('Failed to delete document:', error); - throw error; + console.error('Failed to delete document from backend, falling back to local:', error); + return this._fallbackTo('deleteResultFromDB', id); + } + } + + async clearAllResultsFromDB() { + try { + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('clearAllResultsFromDB'); + } + // 后端没有批量删除接口,逐个删除 + const docs = await this.getAllResultsFromDB(); + for (const doc of docs) { + await this.fetchAPI(`/documents/${doc.id}`, { method: 'DELETE' }); + } + } catch (error) { + console.error('Failed to clear all documents from backend, falling back to local:', error); + return this._fallbackTo('clearAllResultsFromDB'); } } // 术语库 async loadGlossarySets() { try { - if (!AuthManager.isAuthenticated()) return {}; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('loadGlossarySets'); + } const glossaries = await this.fetchAPI('/user/glossaries'); const sets = {}; glossaries.forEach(g => { sets[g.id] = g; }); return sets; } catch (error) { console.error('Failed to load glossaries:', error); - return {}; + return this._fallbackTo('loadGlossarySets'); } } async saveGlossarySets(sets) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('saveGlossarySets', sets); + } // 批量保存(简化实现) for (const [id, set] of Object.entries(sets)) { if (set._isNew) { @@ -210,35 +256,42 @@ class BackendStorage { } } catch (error) { console.error('Failed to save glossaries:', error); - throw error; + return this._fallbackTo('saveGlossarySets', sets); } } // 标注 async saveAnnotationToDB(annotation) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('saveAnnotationToDB', annotation); + } await this.fetchAPI(`/documents/${annotation.documentId}/annotations`, { method: 'POST', body: JSON.stringify(annotation) }); } catch (error) { console.error('Failed to save annotation:', error); - throw error; + return this._fallbackTo('saveAnnotationToDB', annotation); } } async getAnnotationsForDocFromDB(docId) { try { - if (!AuthManager.isAuthenticated()) return []; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('getAnnotationsForDocFromDB', docId); + } return await this.fetchAPI(`/documents/${docId}/annotations`); } catch (error) { console.error('Failed to load annotations:', error); - return []; + return this._fallbackTo('getAnnotationsForDocFromDB', docId); } } // 聊天历史 async loadChatHistory(docId) { try { - if (!AuthManager.isAuthenticated()) return []; + if (!AuthManager.isAuthenticated()) { + // 聊天历史在本地存储中没有对应方法,返回空数组 + return []; + } const data = await this.fetchAPI(`/chat/${docId}/history`); return data.messages || []; } catch (error) { @@ -249,82 +302,95 @@ class BackendStorage { async saveChatMessage(docId, message) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + // 聊天历史在本地存储中没有对应方法 + return; + } await this.fetchAPI(`/chat/${docId}/history`, { method: 'POST', body: JSON.stringify(message) }); } catch (error) { console.error('Failed to save chat message:', error); - throw error; } } async clearChatHistory(docId) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return; + } await this.fetchAPI(`/chat/${docId}/history`, { method: 'DELETE' }); } catch (error) { console.error('Failed to clear chat history:', error); - throw error; } } // 文献引用 async loadReferences(docId) { try { - if (!AuthManager.isAuthenticated()) return []; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('loadReferences', docId) || []; + } return await this.fetchAPI(`/references/${docId}/references`); } catch (error) { console.error('Failed to load references:', error); - return []; + return this._fallbackTo('loadReferences', docId) || []; } } async saveReference(docId, reference) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('saveReference', docId, reference); + } await this.fetchAPI(`/references/${docId}/references`, { method: 'POST', body: JSON.stringify(reference) }); } catch (error) { console.error('Failed to save reference:', error); - throw error; + return this._fallbackTo('saveReference', docId, reference); } } async deleteReference(docId, refId) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('deleteReference', docId, refId); + } await this.fetchAPI(`/references/${docId}/references/${refId}`, { method: 'DELETE' }); } catch (error) { console.error('Failed to delete reference:', error); - throw error; + return this._fallbackTo('deleteReference', docId, refId); } } // Prompt Pool async loadPromptPool() { try { - if (!AuthManager.isAuthenticated()) return { prompts: [], healthConfig: null }; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('loadPromptPool'); + } return await this.fetchAPI('/prompt-pool'); } catch (error) { console.error('Failed to load prompt pool:', error); - return { prompts: [], healthConfig: null }; + return this._fallbackTo('loadPromptPool'); } } async savePromptPool(data) { try { - if (!AuthManager.isAuthenticated()) return; + if (!AuthManager.isAuthenticated()) { + return this._fallbackTo('savePromptPool', data); + } await this.fetchAPI('/prompt-pool', { method: 'PUT', body: JSON.stringify(data) }); } catch (error) { console.error('Failed to save prompt pool:', error); - throw error; + return this._fallbackTo('savePromptPool', data); } } @@ -407,26 +473,16 @@ class StorageAdapterFactory { // ---------------- 初始化与自动切换 ---------------- function printBanner() { - const logoStyle = 'font-size: 16px; font-weight: bold; color: #3b82f6;'; const infoStyle = 'font-size: 14px; color: #10b981;'; const modeStyle = 'font-size: 14px; font-weight: bold; color: #f59e0b;'; const borderStyle = 'color: #6366f1;'; const linkStyle = 'font-size: 13px; color: #06b6d4; text-decoration: underline;'; - const logo = ` - ____ ____ __ __ - | _ \\ __ _ _ __ ___ _ __ | __ ) _ _ _ __ _ __ ___ _ __ \\ \\/ / - | |_) / _\` | '_ \\ / _ \\ '__| | _ \\| | | | '__| '_ \\ / _ \\ '__| \\ / - | __/ (_| | |_) | __/ | | |_) | |_| | | | | | | __/ | / \\ - |_| \\__,_| .__/ \\___|_| |____/ \\__,_|_| |_| |_|\\___|_| /_/\\_\\ - |_| - `; const mode = DEPLOYMENT_MODE === 'backend' ? '后端模式 (Backend Mode)' : '前端模式 (Frontend Mode)'; const storage = DEPLOYMENT_MODE === 'backend' ? 'Backend API + PostgreSQL' : 'localStorage + IndexedDB'; const auth = DEPLOYMENT_MODE === 'backend' ? 'JWT Authentication' : 'No Authentication'; - console.log('%c' + logo, logoStyle); console.log('%c╔════════════════════════════════════════════════════════════╗', borderStyle); console.log('%c║ 系统信息 / System Info ║', borderStyle); console.log('%c╠════════════════════════════════════════════════════════════╣', borderStyle);