diff --git a/core/web_api.py b/core/web_api.py index f55b65b..eba3cb8 100644 --- a/core/web_api.py +++ b/core/web_api.py @@ -21,6 +21,17 @@ from core.activity_recorder import get_recorder from core.embedded_db import EmbeddedGraphDB +def get_resource_path(relative_path): + """获取资源文件的绝对路径,兼容开发环境和PyInstaller打包环境""" + if hasattr(sys, 'frozen'): + # PyInstaller打包后的环境 + base_path = sys._MEIPASS + else: + # 开发环境 + base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + return os.path.join(base_path, relative_path) + + # 登录安全限制 LOGIN_MAX_ATTEMPTS = 5 # 最大尝试次数 LOGIN_WAIT_MINUTES = 5 # 超过次数后等待分钟数 @@ -142,7 +153,7 @@ def save_web_config(updates: dict) -> bool: WEB_CONFIG = load_web_config() -_ui_dir = os.path.join(os.path.dirname(__file__), '..', 'ui') +_ui_dir = get_resource_path('ui') app = Flask(__name__, static_folder=os.path.join(_ui_dir, 'static'), static_url_path='', template_folder=os.path.join(_ui_dir, 'templates')) app.secret_key = WEB_CONFIG.get("SECRET_KEY", "trulymem-secret-key-2026") app.permanent_session_lifetime = timedelta(days=7) diff --git a/ui/app.py b/ui/app.py index 2d2dc00..feb5585 100644 --- a/ui/app.py +++ b/ui/app.py @@ -51,6 +51,9 @@ class GraphMemoryApp(App): initial_config.api_key = api_config.get("api_key", "") initial_config.base_url = api_config.get("base_url", "https://api.deepseek.com") initial_config.model = api_config.get("model", "deepseek-v4-flash") + initial_config.enable_web = api_config.get("enable_web", False) + initial_config.web_port = api_config.get("web_port", 4096) + initial_config.enable_tui = api_config.get("enable_tui", True) tool_limits = settings_data.get("tool_limits", {}) initial_config.persona_update_max = tool_limits.get("persona_update_max", 1) @@ -144,6 +147,23 @@ class GraphMemoryApp(App): welcome_msg += f"API Key: {'已配置' if self._api_configured else '未配置'}\n\n输入消息开始对话" welcome = Message(role="assistant", content=welcome_msg) history.add_message(welcome) + + # 自动启动Web服务(如果配置中启用了) + if self._backend_client: + settings_result = self._backend_client.get_settings() + settings_data = settings_result.get("data", {}) + api_config = settings_data.get("api_config", {}) + enable_web = api_config.get("enable_web", False) + web_port = api_config.get("web_port", 4096) + + # 更新状态栏的Web服务状态 + try: + status_bar.set_web_status(self._web_running, web_port if self._web_running else 0) + except: + pass + + if enable_web: + self._start_web_server(web_port) def _start_web_server(self, port: int = 4096) -> None: """在当前进程通过线程启动 Web 服务(无需子进程)""" @@ -153,10 +173,20 @@ class GraphMemoryApp(App): try: from web_api import run_web_server + from .widgets.status_bar import StatusBar + run_web_server(port=port) self._web_running = True if self._backend_client: self._backend_client.report_web_status(True, port) + + # 更新状态栏 + try: + status_bar = self.query_one(StatusBar) + status_bar.set_web_status(True, port) + except: + pass + self.notify(f"Web 服务已启动 → http://0.0.0.0:{port}", title="Web 服务") except Exception as e: self.notify(f"启动 Web 服务失败: {e}", severity="error") @@ -166,12 +196,22 @@ class GraphMemoryApp(App): if self._web_running: try: from web_api import stop_web_server + from .widgets.status_bar import StatusBar + stop_web_server() except Exception: pass self._web_running = False if self._backend_client: self._backend_client.report_web_status(False, 0) + + # 更新状态栏 + try: + status_bar = self.query_one(StatusBar) + status_bar.set_web_status(False) + except: + pass + self.notify("Web 服务已停止", title="Web 服务") def on_unmount(self) -> None: @@ -370,6 +410,12 @@ class GraphMemoryApp(App): self._start_web_server(config.web_port) else: self._stop_web_server() + + # 更新状态栏的Web服务状态 + try: + status_bar.set_web_status(self._web_running, config.web_port if self._web_running else 0) + except: + pass self.notify("✅ 配置已保存并生效", title="配置成功", severity="information") else: diff --git a/ui/widgets/status_bar.py b/ui/widgets/status_bar.py index f7168be..6b71bc7 100644 --- a/ui/widgets/status_bar.py +++ b/ui/widgets/status_bar.py @@ -18,6 +18,7 @@ class StatusBar(Static): self._shortcuts = "F1:帮助 F2:侧边栏 F3:工具详情 F5:清屏 F6:退出" self._license_info = "本项目由jianf设计,以GPLv3形式开源" self._api_status = "未配置" + self._web_status = "未启动" self._processing = False def on_mount(self) -> None: @@ -27,8 +28,9 @@ class StatusBar(Static): def _update_display(self) -> None: """更新显示""" status_icon = "●" if self._api_status == "已配置" else "○" + web_icon = "●" if self._web_status != "未启动" else "○" processing_indicator = " [处理中...]" if self._processing else "" - self.update(f"{status_icon} API: {self._api_status}{processing_indicator} | {self._license_info} | {self._shortcuts}") + self.update(f"{status_icon} API: {self._api_status} | {web_icon} Web: {self._web_status}{processing_indicator} | {self._license_info} | {self._shortcuts}") def set_api_status(self, configured: bool) -> None: """设置API状态""" @@ -39,3 +41,11 @@ class StatusBar(Static): """设置处理状态""" self._processing = processing self._update_display() + + def set_web_status(self, running: bool, port: int = 0) -> None: + """设置Web服务状态""" + if running: + self._web_status = f"运行中(:{port})" + else: + self._web_status = "未启动" + self._update_display()