qinglong
|
9c1a1d40e4
|
style: 导入卡片间距 gap 12→16px + align stretch 等高
|
2026-06-13 18:46:57 +08:00 |
|
qinglong
|
d0206121b1
|
fix: 统计卡片'安装失败'→'启动失败' + 合并 error_count 计数
|
2026-06-13 18:44:20 +08:00 |
|
qinglong
|
e16250c7e7
|
style: 插件导入卡片 CSS — 等高 / 文件按钮 / 纵向布局
|
2026-06-13 18:43:30 +08:00 |
|
qinglong
|
b2021a45ce
|
feat: 插件管理 — 导入安装 + 统计面板
新增 API:
- GET /api/plugins/stats — 统计 (总计/已启用/已停用/安装失败)
- POST /api/plugins/import — ZIP上传 或 Git clone 安装
- 自动 pip install -r requirements.txt (子进程隔离)
- 自动加载插件 (isolated 模式, 崩溃不影响框架)
- 安装失败记录到 _IMPORT_FAILS
前端:
- 横版统计卡片 (总数/启用/停用/失败)
- ZIP 上传按钮 + Git URL 输入
- 安装状态实时反馈
安全:
- 所有端点 panel_auth 保护
- 子进程安装 (pip/git), 超时120s
- 仅接受 .zip 文件
- 目录冲突检测
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 18:39:38 +08:00 |
|
qinglong
|
a2bd6486a0
|
fix: 动画改用 setInterval (兼容动态 script 加载) + try-catch 保护
|
2026-06-13 18:29:11 +08:00 |
|
qinglong
|
c4b44e0fd2
|
fix: 仪表盘动画改为模块方法 _animateNumber (避免全局作用域问题)
|
2026-06-13 18:26:38 +08:00 |
|
qinglong
|
81ebbc3e45
|
feat: WebUI 首页仪表盘 — CPU/MEM/进程内存 数字跳动动画
- animateNumber(): 900ms ease-out 缓动, 存储前值于 data-val
- CPU%、MEM%、进程内存 三个指标使用跳动动画
- 与 Sentinel 卡片动画风格统一
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 18:21:05 +08:00 |
|
qinglong
|
3ca001b938
|
fix: 进度条平滑过渡 — force reflow 技巧确保 CSS transition 生效
- 临时关 transition → 设旧值 → offsetWidth reflow → 开 transition → 设新值
- DOM 复用: 首次 innerHTML 创建, 后续只改属性
- 进度条从旧值滑到新值, 不再闪现
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 18:19:07 +08:00 |
|
qinglong
|
417213096f
|
fix: Sentinel 清除占位卡片 + 动画延长至 1s
|
2026-06-13 18:17:26 +08:00 |
|
qinglong
|
f3a6e5fbcd
|
feat: Sentinel 数字跳动动画 + 进度条平滑过渡
- 数字 count-up/down: requestAnimationFrame + ease-out cubic, 350ms
- 中间值过渡: 15%→22% 会显示 15.2,16.1,17.5...22.0
- 进度条: CSS transition 自动处理宽度变化
- 卡片 DOM 复用: 更新值而非重建 innerHTML
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 18:12:37 +08:00 |
|
qinglong
|
1d0f227267
|
fix: API Key 删除/创建去掉 CSRF — panel_token HttpOnly 前端无法读取
SameSite=Lax cookie 已防跨站 POST/DELETE, 无需额外 CSRF header
前端 JS 无法读取 HttpOnly cookie, 无法设置 X-CSRF-Token
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 18:07:47 +08:00 |
|
qinglong
|
51c1ee84f0
|
feat: Sentinel 卡片数据刷新动画
- 卡片更新时背景闪烁 (card-flash keyframe)
- 进度条平滑过渡 (cubic-bezier .6s)
- 数值涨跌颜色: 绿涨↑ 红跌↓ 不变灰色
- 渐变进度条背景
- 存储前值用于趋势对比
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 18:06:06 +08:00 |
|
qinglong
|
c416b04798
|
fix: Sentinel 改用前端轮询替代 SSE (解决 EventSource 认证问题)
SSE 跨 panel_auth 认证层不可靠 → 改用 setInterval 每 2s fetch /sentinel/api/nodes
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 18:00:34 +08:00 |
|
qinglong
|
a21e092207
|
fix: Sentinel SSE URL — /SenSu/plugin/... → /plugin/... (匹配实际路由)
|
2026-06-13 17:56:57 +08:00 |
|
qinglong
|
b53410bc77
|
fix: Sentinel SSE 每次轮询都推送 (实时性) + isolation 默认 false + 文档
Sentinel:
- poll_interval 3s→2s, 每次轮询都推 SSE (不再等状态变化)
- 解决稳定状态下数据更新不实时问题
插件系统:
- 默认 isolation: false (避免插件 WebUI 不显示)
- example_plugin 显式设置 isolation: false
- 插件开发指南: settings 示例增加 isolation 说明
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 17:54:39 +08:00 |
|
qinglong
|
af16549524
|
chore: 清理误提交的 base_config.yaml.bak
|
2026-06-13 17:46:33 +08:00 |
|
qinglong
|
5e06c17b52
|
fix: WebSocket 鉴权 — Cookie 优先 (支持 HttpOnly)
根因: panel_token 是 HttpOnly cookie, JS getCookie() 读不到
→ 前端 WS ?token= 参数为空 → 认证失败 → 仪表盘无数据
修复:
- _ws_auth_wrapper: Cookie 优先 → query token 备用 → API Key
- 浏览器同源 WS 自动发送 Cookie, 无需 ?token=
- dashboard.js / logs.js 移除多余的 getCookie+?token=
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 17:45:58 +08:00 |
|
qinglong
|
557135c376
|
security: 配置文件模板化 — base_config.yaml 不再入库
- base_config.yaml → .gitignore (含真实密码不入库)
- 新增 base_config.yaml.example 模板 (password: CHANGE_ME)
- InitService: 检测缺失时自动从模板复制
- 首次启动自动创建配置文件
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 15:20:14 +08:00 |
|
qinglong
|
a2361cf452
|
fix: Sentinel WebUI — 合并为单页面(仪表盘+节点管理Tab)
插件 Web 页面 key 必须匹配 plugin_name (/plugin/sentinel)
改为单页面 Tab 切换: 仪表盘 + 节点管理
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 15:17:17 +08:00 |
|
qinglong
|
634496d975
|
security: Sentinel 路由鉴权 + API Key CSRF + SSRF 防护
修复:
- Sentinel 路由 require_auth=False → True (严重: 未认证可访问)
- API Key 创建/删除加 csrf_protect=True
- SSRF 防护: 测试连接仅允许 http/https scheme
- 插件网络鉴权: 已加载插件自动放行 (不再要求 plugin.network.access)
- Sentinel 权限列表补充 plugin.network.access
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 15:13:56 +08:00 |
|
qinglong
|
5e74b0aad7
|
feat: 🛡️ Sentinel 集群监控插件 + API Key 系统 + 框架设置页
== API Key 系统 ==
- services/web_panel/routes/apikeys.py: CRUD + 权限模板 + 过期控制
- panel_auth + _check_plugin_auth 双重验证 (Session → API Key)
- 模板: readonly(只读) / monitor(监控) / full(管理)
- 持久化到 SenSuDB + 过期自动清理
== WebUI 框架设置页 ==
- static/web_panel/pages/settings.html: API Key 管理界面
- 创建对话框 (名称/权限/TTL), 列表表格, 一键删除
== 热重载修复 ==
- PluginService._reload_plugin: 路由器冻结时跳过 (避免破坏已注册路由)
== 🛡️ Sentinel 插件 ==
- 多节点 SenSu 集群性能监控
- 后台 urllib+run_in_executor 轮询 (避免事件循环死锁)
- SSE 实时推送仪表盘
- 节点管理页 (增删改+连接测试+备注)
- 路径: /sentinel/api/nodes
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 15:05:38 +08:00 |
|
qinglong
|
66ad51002c
|
feat: WebUI 框架设置页面 + API Key 管理 UI
新增:
- static/web_panel/pages/settings.html — API Key CRUD 界面
- 侧边栏 '框架设置' 导航项 (齿轮图标)
- BUILTIN_PAGES 增加 'settings'
- UI 功能: 创建对话框(名称/权限/过期), 列表表格, 一键删除
权限模板:
- readonly — framework.status.read + plugin.info.read
- monitor — + framework.event.subscribe
- full — admin (全部)
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 14:28:59 +08:00 |
|
qinglong
|
b92b1cc4fc
|
security: API Key 权限范围 + 过期时间 + 用量追踪
增强:
- 权限模板: readonly / monitor / full
- 每 key 独立权限列表, 注入 user['perms']
- 可选过期时间 (ttl 参数)
- 过期 key 自动清理 (加载时 + 验证时)
- request_count 用量追踪
- 标记 is_api_key = True
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 14:27:07 +08:00 |
|
qinglong
|
9f6eb29820
|
feat: API Key 系统 — CRUD + panel_auth 集成 + 持久化
新增:
- services/web_panel/routes/apikeys.py — 创建/列表/删除 API Key
- panel_auth() 双重验证: Session Store → API Key fallback
- _check_plugin_auth() 支持 API Key
- API Key 持久化到 SenSuDB (config_kv 表)
- 格式: sk- + 48 hex chars
- 脱敏显示 (前8后4)
- 删除后立即失效 (401)
WebPanelManager 注册 apikeys 路由
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 14:25:55 +08:00 |
|
qinglong
|
e88d472c63
|
docs: API 认证与数据访问全流程文档
|
2026-06-13 14:14:34 +08:00 |
|
qinglong
|
2da52e80d0
|
security: P3+ — 面板密码哈希 + 上传类型白名单 + CSRF Token
面板密码安全:
- 支持哈希存储 (password_hash/password_salt)
- 登录使用 secrets.compare_digest 时序安全比较
- 回退明文兼容旧配置
- 默认密码 admin 时打印 CRITICAL 警告
文件上传安全:
- 拒绝危险扩展名: .exe/.dll/.so/.sh/.bat/.ps1 等
- 拒绝敏感文件名: .htaccess/Makefile/Dockerfile 等
- 上传时检查并返回 403
CSRF 防护:
- panel_auth(csrf_protect=True) 参数
- 写操作需 X-CSRF-Token header 匹配 session token
- 保护范围: 文件删除/写入/创建/上传/重命名 + 项目启停 + 代理管理
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 14:07:38 +08:00 |
|
qinglong
|
d835b66ca9
|
chore: gitignore data/ 目录 (运行时数据,不应入库)
|
2026-06-13 13:57:12 +08:00 |
|
qinglong
|
ff02b2e16d
|
security: P3 — 插件隔离默认开启 + SenSuDB 注册 + 审计日志
- plugins.isolation 默认 true (生产环境进程隔离)
- main.py 注册 SenSuDB 服务
- WebPanelManager 注入 sensu_db 到 app context
- 文件删除/写入操作写入审计日志 (audit_log 表)
- 修复 Session 持久化所需的数据库依赖
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 13:56:58 +08:00 |
|
qinglong
|
c1d2767f52
|
fix: web 命令端点 execute_command → process_command (方法名错误)
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 13:54:37 +08:00 |
|
qinglong
|
fcac02d920
|
security: P2 生产深度加固 — 路径/鉴权/脱敏/校验/持久化/过期
4.3 文件管理器路径收紧:
- 默认移除 Path('/') 全文件系统访问
- 仅允许项目目录 + data/ + 环境变量 SENSU_FILE_ROOTS 指定路径
3.4 插件路由鉴权修复:
- _check_plugin_auth 增加 panel_token 用户身份验证
- 先验证用户登录, 再检查插件权限
4.2 错误脱敏:
- security middleware 捕获异常 → 通用 'Internal server error'
- 堆栈详情仅写入日志, 不暴露给客户端
4.4 命令参数校验:
- POST /api/command 拒绝 shell 元字符 (;&|`$(){}!#~<>)
- 防止命令注入
4.5 Session 持久化:
- 登录/退出时保存到 SenSuDB.config_kv
- 框架重启后自动恢复已持久化会话
4.6 Token 过期: 24h → 2h
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 13:50:51 +08:00 |
|
qinglong
|
2f9063d6d4
|
fix: 登录频率限制 bug — lock_until=0 导致计数被清零
根因: 初始 lock_until=0 时 now >= 0 + 60 恒为真, 每次请求都清除计数
修复: lock_until 改用 None 表示未锁定, 仅非 None 时做过期判断
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 13:46:29 +08:00 |
|
qinglong
|
a75b9d0975
|
security: 前端 WebSocket 连接传递 panel_token 认证
- dashboard.js / logs.js: WS URL 追加 ?token= 查询参数
- app.js: 添加 getCookie() 工具函数
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 13:38:38 +08:00 |
|
qinglong
|
f7b4908322
|
security: 公网生产环境加固 — P0/P1 全部修复
API 认证 (16 个未保护端点 → 全部加 panel_auth):
- 文件管理器: 11 端点 (list/mkdir/delete/upload/download/read/write/...)
- 项目管理: 6 端点 (list/run/stop/logs/stdin/page), 修复硬编码路径前缀
- 代理管理: 3 端点 (list/add/remove)
- 系统状态: 2 HTTP + 1 WS (token 校验)
- 插件页面: 3 端点 (page/sse/event)
- 内部路由: 3 端点 (plugins/commands/data)
认证系统加固:
- 密码哈希: 固定盐 → 每用户独立 secrets.token_hex(16) 随机盐
- 默认密码警告: 启动时检测并打印 critical 级别日志
- 登录频率限制: 5 次失败 / IP → 锁定 60 秒, 返回 429
基础设施:
- 安全响应头: X-Content-Type-Options/X-Frame-Options/X-XSS-Protection/Referrer-Policy
- WebSocket 鉴权: query string token 校验
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 13:37:14 +08:00 |
|
qinglong
|
2dc3c16787
|
fix: 系统监控改用 asyncio task 驱动,抗 Android 后台挂起
- SystemMonitor/PluginStatusPanel: 移除 set_interval, 改用外部 refresh()
- TuiService._refresh_monitors(): asyncio.sleep(2) 循环刷新
- asyncio.sleep 在 Android 恢复前台后能正常续跑
- shutdown 时 cancel refresh task
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 12:50:06 +08:00 |
|
qinglong
|
10d1a1fcb1
|
fix: 标题栏 背景改 + 行高加到2 增强可见性
|
2026-06-13 12:18:50 +08:00 |
|
qinglong
|
feca81842b
|
fix: 回到 grid 布局 + 顶部留空行 — 5行: 空/标题/状态栏/主区/输入
grid-rows: 1 1 auto 1fr 3
- Row 1: 空白行
- Row 2: 标题栏 (Static, bold)
- Row 3: 状态栏 (auto 高度)
- Row 4: 主显示区 (1fr 填满剩余)
- Row 5: 输入栏 (固定3行)
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 12:17:30 +08:00 |
|
qinglong
|
ae8586bc4a
|
fix: 用 Static 标题栏替代 Textual Header,确保始终显示
- 自定义 #title-bar (dock top, height 1, bold)
- 移除不可靠的 Header widget
- _setup_title 更新 title_bar 内容
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 12:00:36 +08:00 |
|
qinglong
|
18f9fcfcd3
|
fix: Header dock top 防止被 monitor-area 遮挡
|
2026-06-13 11:56:58 +08:00 |
|
qinglong
|
f7117825ae
|
fix: 日志区改用 dock 布局自动占满剩余空间
- monitor-area: dock top
- input-area: dock bottom, height 3
- main-area: 自动填充中间所有空白
- 去掉 grid 布局避免高度塌缩
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:54:27 +08:00 |
|
qinglong
|
7caf35281b
|
fix: 移除输入区外部边框
|
2026-06-13 11:52:00 +08:00 |
|
qinglong
|
74fd337a4c
|
fix: 输入框高度 5→3 + 主显示区 min-height 防塌缩
|
2026-06-13 11:49:57 +08:00 |
|
qinglong
|
d88438be3b
|
style: Header 与状态栏之间加一行间距 (padding-top: 1)
|
2026-06-13 11:46:23 +08:00 |
|
qinglong
|
9517afabd9
|
fix: 输入框置底 — 移除冲突的 height 属性,用 align center bottom
grid-rows 已设置行高 (auto auto 1fr 5),CSS height 会与 grid 冲突
改为 align: center bottom 让 Input 贴底
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:46:11 +08:00 |
|
qinglong
|
cbc2dd6b82
|
fix: TUI 布局调整 — 标题恢复 + 增加输入/状态栏高度
- 恢复 Header 标题栏
- 4 行布局: auto(Header) + auto(Monitor) + 1fr(Main) + 5(Input)
- 状态栏高度 2→3 行
- 输入栏高度 3→5 行
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:42:08 +08:00 |
|
qinglong
|
6382c8019d
|
refactor: TUI 布局重构 — 经典三行:状态栏 + 主显示区 + 固定底部输入
布局改为 grid-rows: auto 1fr 3:
- Row 1 (auto): 系统监控 + 插件状态 紧凑状态栏
- Row 2 (1fr): 合并日志/消息为单一可滚动主显示区
- Row 3 (3 lines): 固定高度输入栏,始终可见
同时:
- 移除 Header/Footer 节省空间
- show_message 路由到 log_display
- 清理未使用的 message_display 分离逻辑
- 移除未使用的 reactive/Horizontal imports
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:34:45 +08:00 |
|
qinglong
|
7518f4fd17
|
fix: textual.suggestion 兼容老版 Textual + 升级最低版本约束
- textual.suggestion 在 Textual < 2.0 中不存在
- 添加 try/except fallback 构造兼容 Suggestion 类
- requirements.txt: textual>=0.40.0 → >=0.47.0
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:15:09 +08:00 |
|
qinglong
|
249ef5d91e
|
docs: 更新 ROADMAP v0.7.0 — 反映全部已实现功能
v0.2.2-v0.5 所有里程碑已完成
版本号: Alpha 0.2.1 → 0.7.0
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:09:15 +08:00 |
|
qinglong
|
fca2aa5a05
|
feat(v0.5.2): 插件索引仓库 + install 命令
PluginIndex 服务:
- 从远程 JSON 索引获取可用插件列表
- 支持 zip 包和单文件 .py 两种格式安装
- 自动解压/移动到 plugins/ 目录
- 默认索引地址: GitHub Pages
install 内置命令:
- install --list → 列出远程可用插件
- install <name> → 下载并安装
- 自动缓存索引,避免重复请求
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:08:47 +08:00 |
|
qinglong
|
7ce495de2a
|
feat(v0.5.1): 插件进程隔离 — 集成到 PluginService
- PluginService._should_isolate() 检查全局配置 + 插件级配置
- _load_plugin_isolated() 使用 IsolatedPlugin (multiprocessing.Process)
- unload_plugin() 兼容隔离/非隔离两种实例
- 配置: plugins.isolation (全局默认) + settings.isolation (插件级)
- 插件崩溃不影响框架主进程
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:07:30 +08:00 |
|
qinglong
|
37d1110f2b
|
feat(v0.4.3): TUI 命令 Tab 补全
CommandSuggester 实现 Textual Suggester API:
- 根据已注册命令列表提供前缀匹配补全
- 前缀无匹配时 fallback 到包含匹配
- 按长度排序,优先最短匹配
- 在 Input placeholder 提示 'Tab 补全'
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-06-13 11:06:31 +08:00 |
|