From e28f5a1ebdde6cd7cef69dab7549ea6524760dc6 Mon Sep 17 00:00:00 2001 From: Minidoracat Date: Sun, 15 Jun 2025 13:45:38 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A8=20=E6=9C=83=E8=A9=B1=E6=AD=B7?= =?UTF-8?q?=E5=8F=B2=E6=94=B9=E7=82=BA=E6=9C=AC=E5=9C=B0=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=EF=BC=8C=E5=BB=A2=E9=99=A4=20localstorage=20=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/routes/main_routes.py | 85 ++++++++ .../web/static/js/modules/session-manager.js | 19 ++ .../modules/session/session-data-manager.js | 198 ++++++++++++------ 3 files changed, 241 insertions(+), 61 deletions(-) diff --git a/src/mcp_feedback_enhanced/web/routes/main_routes.py b/src/mcp_feedback_enhanced/web/routes/main_routes.py index 973ac99..507feac 100644 --- a/src/mcp_feedback_enhanced/web/routes/main_routes.py +++ b/src/mcp_feedback_enhanced/web/routes/main_routes.py @@ -306,6 +306,91 @@ def setup_routes(manager: "WebUIManager"): content={"status": "error", "message": f"清除失敗: {e!s}"}, ) + @manager.app.get("/api/load-session-history") + async def load_session_history(): + """從檔案載入會話歷史""" + try: + # 使用統一的設定檔案路徑 + config_dir = Path.home() / ".config" / "mcp-feedback-enhanced" + history_file = config_dir / "session_history.json" + + if history_file.exists(): + with open(history_file, encoding="utf-8") as f: + history_data = json.load(f) + + debug_log(f"會話歷史已從檔案載入: {history_file}") + + # 確保資料格式相容性 + if isinstance(history_data, dict): + # 新格式:包含版本資訊和其他元資料 + sessions = history_data.get("sessions", []) + last_cleanup = history_data.get("lastCleanup", 0) + else: + # 舊格式:直接是會話陣列(向後相容) + sessions = history_data if isinstance(history_data, list) else [] + last_cleanup = 0 + + # 回傳與 localStorage 格式相容的資料 + return JSONResponse( + content={"sessions": sessions, "lastCleanup": last_cleanup} + ) + + debug_log("會話歷史檔案不存在,返回空歷史") + return JSONResponse(content={"sessions": [], "lastCleanup": 0}) + + except Exception as e: + debug_log(f"載入會話歷史失敗: {e}") + return JSONResponse( + status_code=500, + content={"status": "error", "message": f"載入失敗: {e!s}"}, + ) + + @manager.app.post("/api/save-session-history") + async def save_session_history(request: Request): + """保存會話歷史到檔案""" + try: + data = await request.json() + + # 使用統一的設定檔案路徑 + config_dir = Path.home() / ".config" / "mcp-feedback-enhanced" + config_dir.mkdir(parents=True, exist_ok=True) + history_file = config_dir / "session_history.json" + + # 建立新格式的資料結構 + history_data = { + "version": "1.0", + "sessions": data.get("sessions", []), + "lastCleanup": data.get("lastCleanup", 0), + "savedAt": int(time.time() * 1000), # 當前時間戳 + } + + # 如果是首次儲存且有 localStorage 遷移標記 + if not history_file.exists() and data.get("migratedFrom") == "localStorage": + history_data["migratedFrom"] = "localStorage" + history_data["migratedAt"] = int(time.time() * 1000) + + # 保存會話歷史到檔案 + with open(history_file, "w", encoding="utf-8") as f: + json.dump(history_data, f, ensure_ascii=False, indent=2) + + debug_log(f"會話歷史已保存到: {history_file}") + session_count = len(history_data["sessions"]) + debug_log(f"保存了 {session_count} 個會話記錄") + + return JSONResponse( + content={ + "status": "success", + "message": f"會話歷史已保存({session_count} 個會話)", + } + ) + + except Exception as e: + debug_log(f"保存會話歷史失敗: {e}") + return JSONResponse( + status_code=500, + content={"status": "error", "message": f"保存失敗: {e!s}"}, + ) + @manager.app.get("/api/active-tabs") async def get_active_tabs(): """獲取活躍標籤頁信息 - 優先使用全局狀態""" diff --git a/src/mcp_feedback_enhanced/web/static/js/modules/session-manager.js b/src/mcp_feedback_enhanced/web/static/js/modules/session-manager.js index 4f7bc59..75ad7a4 100644 --- a/src/mcp_feedback_enhanced/web/static/js/modules/session-manager.js +++ b/src/mcp_feedback_enhanced/web/static/js/modules/session-manager.js @@ -74,6 +74,9 @@ }, onStatsChange: function(stats) { self.handleStatsChange(stats); + }, + onDataChanged: function() { + self.handleDataChanged(); } }); }; @@ -115,6 +118,22 @@ this.uiRenderer.renderStats(stats); }; + /** + * 處理資料變更(用於異步載入完成後的更新) + */ + SessionManager.prototype.handleDataChanged = function() { + console.log('📋 處理資料變更,重新渲染所有內容'); + + // 重新渲染所有內容 + const currentSession = this.dataManager.getCurrentSession(); + const history = this.dataManager.getSessionHistory(); + const stats = this.dataManager.getStats(); + + this.uiRenderer.renderCurrentSession(currentSession); + this.uiRenderer.renderSessionHistory(history); + this.uiRenderer.renderStats(stats); + }; + /** * 設置事件監聽器 */ diff --git a/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js b/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js index fb53b1c..2172fae 100644 --- a/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js +++ b/src/mcp_feedback_enhanced/web/static/js/modules/session/session-data-manager.js @@ -32,19 +32,18 @@ averageDuration: 0 }; - // localStorage 相關設定 - this.localStorageKey = 'mcp-session-history'; + // 設定管理器 this.settingsManager = options.settingsManager || null; // 回調函數 this.onSessionChange = options.onSessionChange || null; this.onHistoryChange = options.onHistoryChange || null; this.onStatsChange = options.onStatsChange || null; + this.onDataChanged = options.onDataChanged || null; // 初始化:載入歷史記錄並清理過期資料 - this.loadFromLocalStorage(); - this.cleanupExpiredSessions(); - this.updateStats(); + // 注意:loadFromServer 是異步的,會在載入完成後自動觸發更新 + this.loadFromServer(); console.log('📊 SessionDataManager 初始化完成'); } @@ -227,8 +226,8 @@ this.sessionHistory = this.sessionHistory.slice(0, 10); } - // 保存到 localStorage - this.saveToLocalStorage(); + // 保存到伺服器端 + this.saveToServer(); this.updateStats(); @@ -390,8 +389,8 @@ } }); - // 保存到 localStorage - this.saveToLocalStorage(); + // 保存到伺服器端 + this.saveToServer(); console.log('📊 所有用戶訊息記錄已清空'); return true; @@ -410,7 +409,7 @@ if (session && session.user_messages) { session.user_messages = []; - this.saveToLocalStorage(); + this.saveToServer(); console.log('📊 會話用戶訊息記錄已清空:', sessionId); return true; } @@ -458,9 +457,17 @@ this.sessionStats.todayCount = todaySessions.length; // 計算今日平均持續時間 - const todayCompletedSessions = todaySessions.filter(s => s.duration && s.duration > 0); + const todayCompletedSessions = todaySessions.filter(function(s) { + // 過濾有效的持續時間:大於 0 且小於 24 小時(86400 秒) + return s.duration && s.duration > 0 && s.duration < 86400; + }); + if (todayCompletedSessions.length > 0) { - const totalDuration = todayCompletedSessions.reduce((sum, s) => sum + s.duration, 0); + const totalDuration = todayCompletedSessions.reduce(function(sum, s) { + // 確保持續時間是合理的數值 + const duration = Math.min(s.duration, 86400); // 最大 24 小時 + return sum + duration; + }, 0); this.sessionStats.averageDuration = Math.round(totalDuration / todayCompletedSessions.length); } else { this.sessionStats.averageDuration = 0; @@ -495,8 +502,8 @@ SessionDataManager.prototype.clearHistory = function() { this.sessionHistory = []; - // 清空 localStorage - this.clearLocalStorage(); + // 清空伺服器端資料 + this.clearServerData(); this.updateStats(); if (this.onHistoryChange) { @@ -562,64 +569,133 @@ }; /** - * 從 localStorage 載入會話歷史 + * 從伺服器載入會話歷史 */ - SessionDataManager.prototype.loadFromLocalStorage = function() { - if (!window.localStorage) { - console.warn('📊 localStorage 不可用'); - return; - } + SessionDataManager.prototype.loadFromServer = function() { + const self = this; - try { - const stored = localStorage.getItem(this.localStorageKey); - if (stored) { - const data = JSON.parse(stored); - if (data && Array.isArray(data.sessions)) { - this.sessionHistory = data.sessions; - console.log('📊 從 localStorage 載入', this.sessionHistory.length, '個會話'); + fetch('/api/load-session-history') + .then(function(response) { + if (response.ok) { + return response.json(); + } else { + throw new Error('伺服器回應錯誤: ' + response.status); } + }) + .then(function(data) { + if (data && Array.isArray(data.sessions)) { + self.sessionHistory = data.sessions; + console.log('📊 從伺服器載入', self.sessionHistory.length, '個會話'); + + // 載入完成後進行清理和統計更新 + self.cleanupExpiredSessions(); + self.updateStats(); + + // 觸發歷史記錄變更回調 + if (self.onHistoryChange) { + self.onHistoryChange(self.sessionHistory); + } + + // 觸發資料變更回調 + if (self.onDataChanged) { + self.onDataChanged(); + } + } else { + console.warn('📊 伺服器回應格式錯誤:', data); + self.sessionHistory = []; + + // 即使沒有資料也要更新統計 + self.updateStats(); + + // 觸發歷史記錄變更回調(空列表) + if (self.onHistoryChange) { + self.onHistoryChange(self.sessionHistory); + } + + if (self.onDataChanged) { + self.onDataChanged(); + } + } + }) + .catch(function(error) { + console.warn('📊 從伺服器載入會話歷史失敗:', error); + self.sessionHistory = []; + + // 載入失敗時也要更新統計 + self.updateStats(); + + // 觸發歷史記錄變更回調(空列表) + if (self.onHistoryChange) { + self.onHistoryChange(self.sessionHistory); + } + + if (self.onDataChanged) { + self.onDataChanged(); + } + }); + }; + + /** + * 保存會話歷史到伺服器 + */ + SessionDataManager.prototype.saveToServer = function() { + const data = { + sessions: this.sessionHistory, + lastCleanup: TimeUtils.getCurrentTimestamp() + }; + + fetch('/api/save-session-history', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data) + }) + .then(function(response) { + if (response.ok) { + console.log('📊 已保存', data.sessions.length, '個會話到伺服器'); + return response.json(); + } else { + throw new Error('伺服器回應錯誤: ' + response.status); } - } catch (error) { - console.error('📊 從 localStorage 載入會話歷史失敗:', error); - } + }) + .then(function(result) { + console.log('📊 伺服器保存回應:', result.message); + }) + .catch(function(error) { + console.error('📊 保存會話歷史到伺服器失敗:', error); + }); }; /** - * 保存會話歷史到 localStorage + * 清空伺服器端的會話歷史 */ - SessionDataManager.prototype.saveToLocalStorage = function() { - if (!window.localStorage) { - console.warn('📊 localStorage 不可用'); - return; - } + SessionDataManager.prototype.clearServerData = function() { + const emptyData = { + sessions: [], + lastCleanup: TimeUtils.getCurrentTimestamp() + }; - try { - const data = { - sessions: this.sessionHistory, - lastCleanup: TimeUtils.getCurrentTimestamp() - }; - localStorage.setItem(this.localStorageKey, JSON.stringify(data)); - console.log('📊 已保存', this.sessionHistory.length, '個會話到 localStorage'); - } catch (error) { - console.error('📊 保存會話歷史到 localStorage 失敗:', error); - } + fetch('/api/save-session-history', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(emptyData) + }) + .then(function(response) { + if (response.ok) { + console.log('📊 已清空伺服器端的會話歷史'); + } else { + throw new Error('伺服器回應錯誤: ' + response.status); + } + }) + .catch(function(error) { + console.error('📊 清空伺服器端會話歷史失敗:', error); + }); }; - /** - * 清空 localStorage 中的會話歷史 - */ - SessionDataManager.prototype.clearLocalStorage = function() { - if (!window.localStorage) { - return; - } - try { - localStorage.removeItem(this.localStorageKey); - console.log('📊 已清空 localStorage 中的會話歷史'); - } catch (error) { - console.error('📊 清空 localStorage 失敗:', error); - } - }; /** * 清理過期的會話 @@ -642,7 +718,7 @@ const cleanedCount = originalCount - this.sessionHistory.length; if (cleanedCount > 0) { console.log('📊 清理了', cleanedCount, '個過期會話'); - this.saveToLocalStorage(); + this.saveToServer(); } };