Commit Graph

60 Commits

Author SHA1 Message Date
Nexus Agent 40cba6a567 fix(settings): detect Telegram group chat IDs via my_chat_member
Extend getUpdates parsing for group joins and update settings UI hints so Chat ID detection works for supergroups, not only private DMs.
2026-06-13 02:04:59 +08:00
Nexus Agent 821cf12ade fix(terminal,alerts): quick-cmd enter on send and offline Telegram channel
Append CR when executing quick commands (fixes trim stripping saved \r) and
allow optional dedicated Telegram bot for server offline alerts only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-13 01:45:55 +08:00
Nexus Agent e833b923c8 feat(browser): 远程浏览器 Worker 桥接与顶栏固定悬浮窗
接入 Playwright Worker、会话 WebSocket 与全局浏览器面板(固定于 App Bar 下);
含验证码 stealth、设置项默认音与 URL 安全校验;附带 worker 部署与设计文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 06:49:20 +08:00
Nexus Agent bdbfef7110 fix(audit): 目标列显示服务器名称而非 #id
审计列表对 server 类型批量解析 target_name,前端展示为「服务器 {name}」。
2026-06-12 04:16:46 +08:00
Nexus Agent 704764bd7e fix: 订阅白名单多 worker 同步并统一上传上限 500MB
保存 IP 白名单时同步刷新并 Redis 广播 login_* 设置;推送/文件上传与 Nginx 模板对齐为 500MB,避免生产 50m/100m 导致大文件失败。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 17:39:26 +08:00
Nexus Agent dabfc128b3 feat: 浏览器 RDP、推送提示音与子机指标等多项功能
- 3389远程页 + guacd 侧车 WebSocket 桥(无 RDP 会话审计)
- 文件推送完成独立提示音,至少一台成功即播放
- 子机列表展开 CPU/内存/磁盘 sparkline 与指标采样落库
- 终端全量服务器列表、连接中状态;告警 Telegram 公网 IP
- SSH 密钥凭据 UI 简化;子机搜索未设路径筛选

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-10 01:37:32 +08:00
Nexus Agent 7209f53af9 feat: 子机离线 Telegram 告警与仪表盘统计/趋势增强
离线边沿检测推送一次 Telegram(设置开关 notify_alert_offline);对齐仪表盘与服务器页统计卡、舰队趋势交互与告警叠加。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 17:16:48 +08:00
Nexus Agent 1fefa4b6cb feat(credentials): validate RSA/OPENSSH PEM and auto-derive public key
Accepts keys like nz-admin.pem (BEGIN RSA PRIVATE KEY); rejects invalid PEM
before encrypting. Public key field optional on create/update.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 09:05:06 +08:00
Nexus Agent fe8c11b706 fix(credentials): load SSH key presets via getList and validate save form
The ssh-key-presets API returns a plain array; fetchPagePerPage left the
list empty so new keys appeared not to save.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 09:00:00 +08:00
Nexus Agent e3efbd4552 feat(timezone): operator schedules and filters use Beijing wall clock.
Cron matching, audit/alert date filters, and schedule UI align on Asia/Shanghai
while JWT/TOTP/heartbeat stay UTC; follow-ups add WS alert timestamps, fixed
install timezone copy, and 422 on invalid date filters.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-09 05:50:32 +08:00
Nexus Agent 3864d120fb fix: alerts page size and schedule once fire_at parsing.
Wire alert history items-per-page to the API and parse fire_at ISO strings to datetime so single-shot push schedules save instead of 500.
2026-06-09 02:47:36 +08:00
Nexus Agent 0f6f91e61a fix(api): align alerts/commands contracts and ship full-site test suite.
Restore alert history filters and stats fields, paginate command/session logs for the SPA, show script labels on schedules, and land integration/E2E coverage with rebuilt web assets.
2026-06-09 02:13:13 +08:00
Nexus Agent abeab9d3e4 Fix recovery Telegram dedup and unify Beijing time display.
Store alert metrics without per-value Redis members to stop duplicate recovery pushes; format all operator-facing timestamps in Asia/Shanghai across Web UI and Telegram.
2026-06-08 23:18:56 +08:00
Nexus Agent 27da842a20 fix: Telegram 开关多 worker 同步与服务器行内详情展开
关闭 notify_* 后通过 Redis 广播同步各 worker 内存设置;修复 expanded id 类型不匹配导致点击名称无法展开详情。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 13:05:22 +08:00
Nexus Agent 08a0157c95 feat: 调度表单重构、登录门控、批量任务与页面缓存对齐
调度页执行周期可视化/单次执行/分类选机/推送源对齐;登录 IP 门控与无缝导航;
服务器批量后台任务、执行记录、凭据合并、各页激活刷新与错误提示修复。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 11:17:21 +08:00
Nexus Agent 0b2d7ac50d feat(scripts): 异步执行、全局进度看板与完成提示音设置
脚本 exec 立即返回 running 并在后台跑批次;/ws/alerts 推送 script_progress/complete;
新增 ScriptRunsPage、Telegram 汇总通知与可配置的浏览器完成提示音。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-08 07:07:02 +08:00
Nexus Agent dfb0ea2d8f fix(frontend): 脚本库与推送调度对齐设计文档
修复 ScriptsPage 执行契约(script_id/command、历史过滤、长任务/凭据)及 SchedulesPage 缺失能力(target_path、next_run、push/script、cron/once、sync_mode);后端补迁移与 schedule_runner 传参。
2026-06-08 02:39:18 +08:00
Nexus Agent ea2507dbc0 feat(telegram): 全部通知改为中文详细说明
统一告警/恢复/系统/Layer3/测试消息格式,含来源、影响与建议操作。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-07 21:01:47 +08:00
Nexus Agent f9934bd4f1 feat(ops-patrol): Layer 3 巡检与 Telegram 配置打通
保存 Telegram 时同步至 .env,health_monitor 支持 MySQL 回退,
设置页展示巡检状态,部署时自动安装 cron。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-07 20:17:24 +08:00
Nexus Agent 540a7fce05 fix(settings): Telegram 检测/测试闭环与 8 项告警开关恢复
检测与测试前自动持久化 Token/Chat ID;getUpdates 清除 Webhook 并返回具体 API 错误;恢复 NOTIFY 分项开关与免密 reveal。
2026-06-07 19:45:07 +08:00
Nexus Agent e5e2582743 fix(security): 外网攻击面加固 BL-01~05
收紧 PUBLIC 路径误匹配、强制 health/detail 与壁纸 sync 鉴权、默认关闭 OpenAPI;
安装锁定后 /api/install/* 统一 404;附探测脚本、测试与审计文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-07 01:45:10 +08:00
Nexus Deploy 4ba45abf38 feat(servers): 凭据轮询登录与连接失败列表
- 密码/SSH密钥预设增加用户名;快速添加(IP)轮询全部预设尝试 SSH 登录
- 成功入 servers 列表;失败写入 pending_servers 并支持重试/删除
- 新增 credential_poller、try_ssh_login 与 add-by-ip/pending API
2026-06-06 20:08:48 +08:00
Nexus Deploy b866e944ab fix: Code Review P0/P1 batches 1-3 and single-operator risk acceptance
Apply sync/install/auth/schedule/retry/agent/settings fixes from full
code review; document accepted WS and Agent legacy risks for solo ops.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-04 15:57:49 +08:00
Your Name e3eb0e65a5 fix(ui): 仪表盘/审计/调度/重试修复与终端快捷命令改版
统一终端页与全局侧栏菜单;快捷命令仅在终端执行,增删改排序迁入系统设置;命令输入区加高与 Enter/Shift+Enter 行为;后端自定义命令优先与 reorder API。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 21:56:07 +08:00
Your Name 8311821590 fix: 全量修复批量选择、JWT 60min、script-callback 认证与心跳数据流
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 12:17:47 +08:00
Your Name 6183047fd6 fix: 全站 bug 审查修复 (前端9项 + 后端5项)
Nexus CI/CD / test (push) Waiting to run
Nexus CI/CD / deploy (push) Blocked by required conditions
Nexus Pre-commit Checks / quick-check (push) Waiting to run
前端修复:
- P0: App.vue http 未导入导致全局搜索崩溃
- P1: TOTP 登录流程断裂 (TotpRequiredError 非 ApiError 子类)
- P1: ServersPage 编辑服务器 domain 被端口字符串污染
- P1: PushPage Set[0] 无效 → values().next().value
- P1: SettingsPage API 返回字符串未转数字
- P2: api/index.ts getList 重复定义移除
- P2: LoginPage 锁定倒计时 now ref + watch 更新
- P2: TerminalPage 路径验证正则放松 (允许空格/中文)
- P2: useWebSocket CONNECTING/CLOSING 状态关闭旧连接

后端修复:
- P1: websocket.py redis.keys() → scan_iter() 避免阻塞
- P1: auth_service.py TTL 仅在 key 无 TTL 时设置
- P1: servers.py 批量操作加 asyncio.Lock 避免并发 session commit
- P2: settings.py asyncio.get_event_loop() → get_running_loop()
- P2: settings.py datetime.utcfromtimestamp() → datetime.fromtimestamp(tz=utc)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 11:24:55 +08:00
Your Name 1ecd8e4542 fix: move wallpaper routes before /{key} catch-all to fix 404
Bing wallpaper endpoint (/bing-wallpapers) was returning 404 from HTTP
because FastAPI matched it against the /{key} catch-all route at position
2 before the specific route at position 13 ever got reached.

- Moved wallpaper routes (bing-wallpapers, bing-wallpaper) and helper code
  before the /{key} wildcard route
- Removed dead guard code in get_setting() that was never effective —
  FastAPI route matching happens before the handler body executes
- Removed duplicate wallpaper code block at end of file

Root cause: FastAPI matches routes in registration order. /{key} at
position 2 matched 'bing-wallpapers' as the key parameter, returning
'Setting not found' instead of routing to the wallpaper endpoint.
2026-05-31 22:00:33 +08:00
Your Name c6485168b6 fix: bing-wallpapers route was being caught by /{key} catch-all
FastAPI matches routes in order: /{key} registered before bing-wallpapers
because routes are sorted by path. /{key} matches 'bing-wallpapers' as a
key name, returning 404 'Setting not found' instead of hitting the actual
bing_wallpapers handler.

Fixed by adding a guard in get_setting() to skip these fixed path names,
and crucially by ensuring bing-wallpaper(s) routes are registered BEFORE
the /{key} catch-all in the router.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:59:26 +08:00
Your Name c9baecfdfa feat: Bing wallpaper slideshow — 12 images, hourly rotation, weekly cleanup
- Frontend: fullscreen wallpaper background with 1.5s crossfade transition,
  login card centered with glass effect, images rotate every hour
- Backend: new /api/settings/bing-wallpapers endpoint fetches 8 recent images
  from Bing HPImageArchive, caches to web/app/wallpapers/, returns all URLs
- Auto-cleanup: removes files older than 7 days, keeps max 12 newest
- Old /bing-wallpaper endpoint kept for backward compatibility

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:51:50 +08:00
Your Name 3d5870b404 fix: correct wallpaper dir to 3 levels up (server/api/settings.py -> Nexus/web/app/wallpapers)
Path breakdown: __file__ is server/api/settings.py
  .parent → server/api/
  .parent.parent → server/
  .parent.parent.parent → Nexus/ (repo root)
  Then append web/app/wallpapers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:38:35 +08:00
Your Name afd32118b2 fix: correct wallpaper directory path in settings.py
- Path(__file__) is server/api/settings.py, so:
  .parent → api/  .parent → server/  .parent → Nexus/ (ROOT)
  .parent → (above ROOT — wrong!)
- Changed from .parent.parent.parent to .parent.parent.parent.parent
  to reach the project root, then into web/app/wallpapers/
- Moved _WALLPAPER_DIR.mkdir into the async function body

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:34:27 +08:00
Your Name 04dda2c419 fix: download Bing wallpaper to server disk, auto-clean weekly
- Changed bing-wallpaper endpoint from URL proxy to image downloader
- Saves wallpaper to web/app/wallpapers/YYYY-MM-DD.jpg
- Auto-deletes wallpapers older than 7 days on each request
- Falls back to yesterday's cached file if download fails
- Added Path import for filesystem access

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:23:55 +08:00
Your Name e084d90c61 fix: add Bing wallpaper proxy endpoint, make it public (no JWT required)
- Added GET /api/settings/bing-wallpaper — proxies cn.bing.com HPImageArchive
  to avoid CORS issues, returns {url} for the daily wallpaper
- Added /api/settings/bing-wallpaper to PUBLIC_PREFIXES in auth_jwt.py
  so the login page can fetch it without authentication
- Login page now fetches wallpaper via backend proxy instead of direct CORS-blocked fetch

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:12:09 +08:00
Your Name 293a0d64fe feat: 全站亮色/暗色主题切换
实现方案: CSS自定义属性 + Tailwind arbitrary values

新增文件:
- web/app/vendor/theme.css — CSS变量定义(亮色+暗色)

修改文件 (15个HTML + 2个JS + 2个Python):
- layout.js — 侧边栏🌙主题切换按钮 + loadTheme/toggleTheme
- settings.py — MUTABLE_KEYS加theme/editor_theme
- 15个HTML页面 — bg-slate-*替换为bg-[var(--bg-*)] + 引入theme.css

替换统计:
- index.html: 48处
- servers.html: 183处
- files.html: 94处
- push.html: 157处
- scripts.html: 83处
- credentials.html: 100处
- schedules.html: 59处
- retries.html: 40处
- commands.html: 57处
- alerts.html: 47处
- audit.html: 32处
- settings.html: 125处
- terminal.html: 89处
- install.html: 72处
- login.html: 14处
总计: 1199处替换

功能:
- 侧边栏底部🌙/☀️按钮切换亮色/暗色
- 主题偏好保存到MySQL(settings表)
- 页面加载时自动恢复主题
- Monaco编辑器主题独立控制(⚙️菜单)
2026-05-31 02:55:45 +08:00
Your Name e65c9993a2 feat: 编辑器亮色/暗色主题切换 + 保存到MySQL
- 标题栏🌙/☀️按钮切换 Monaco 主题
- 'vs'(亮色) / 'vs-dark'(暗色)
- 切换后立即应用到所有打开的标签页
- 通过 PUT /api/settings/editor_theme 保存到MySQL
- 页面加载时自动读取保存的主题偏好
- settings.py MUTABLE_KEYS 白名单加 editor_theme
2026-05-31 02:39:21 +08:00
Your Name 64503c0260 fix: IP校验改用endpoint级HTTPException替代Pydantic validator
model_validator/field_validator在自定义RequestValidationError handler下
无法正确序列化为JSON,导致500。改用endpoint级_validate_ip_list()辅助函数
抛HTTPException(400),避免Pydantic→FastAPI序列化链路问题。
2026-05-30 22:48:41 +08:00
Your Name d19f5af807 fix: IP格式校验用field_validator替代model_validator
model_validator(mode='after')抛出ValueError时FastAPI不能正确序列化为422
→ 导致500 TypeError: Object of type ValueError is not JSON serializable
改用field_validator(mode='before')由Pydantic正常捕获返回422
2026-05-30 22:44:26 +08:00
Your Name 36fde6e6ed fix: 审计7个FINDING修复
Nexus CI/CD / test (push) Waiting to run
Nexus CI/CD / deploy (push) Blocked by required conditions
Nexus Pre-commit Checks / quick-check (push) Waiting to run
- F-1 [MEDIUM] IpAllowlistSaveRequest 加 IP/CIDR/域名格式校验
- F-2 [MEDIUM] _is_private_host 改为 async DNS 解析 (不再阻塞事件循环)
- F-3 [LOW] hot-reload int转换失败改 logger.warning 而非静默pass
- F-4 [LOW] 提取 _verify_reauth 辅助函数,消除4处重复re-auth代码
- F-5 [LOW] ChangePasswordRequest.totp_code 加 min_length=6 约束
- F-6 [LOW] revealTelegramToken 空catch改 console.error+toast
- F-7 [LOW] toggleApiKeyVisibility 空catch改 console.error+toast
2026-05-30 22:34:45 +08:00
Your Name a50f17941d fix: settings 安全加固 + UX 迭代 — 10项修复
安全修复:
- S-01 SSRF: parse-subscription 加私有IP封禁+重定向限制+响应大小限制
- S-02 值校验: PUT /{key} 加 key 白名单 + INT 范围校验(1-1000/1-100/10-600)
- S-05 重登录: 改密码成功后跳转login.html+绿色提示
- S-07 审计: add_manual_ips/remove_allowlist_ip 补全审计日志
- S-09 TOTP: 已启用TOTP时改密码需输入验证码

UX改进:
- UX-01: 密码框focus ring + 设置项input type映射(number/url/text)
- UX-02: 改密码按钮loading状态(disabled+提交中...)
- UX-03: 3处空catch块→console.error+toast
- UX-04: 保存失败时reloadSettings恢复原值
- UX-05: IP格式校验(前端正则+后端Pydantic model_validator)
2026-05-30 21:58:13 +08:00
Your Name 5213def150 fix: Telegram Bot Token 脱敏 — GET 不返回明文,reveal 需密码验证
- 后端 SENSITIVE_KEYS 加入 telegram_bot_token,GET /settings/ 返回脱敏值
- 新增 POST /settings/telegram/reveal-token 端点(密码验证 + 审计日志)
- 前端 Telegram 区域独立渲染:Bot Token 默认脱敏,点"显示"需输入密码
- Chat ID 保持可编辑(非敏感信息)
- Bot Token 输入框默认 type=password,可切换明文/遮掩
2026-05-30 21:12:41 +08:00
Your Name 5166660f3a fix: GET /api/settings/ip-allowlist 被 /{key} 路由拦截 → 404
/{key} 路由在 ip-allowlist 之前定义,FastAPI 将 ip-allowlist
当作 setting key 查找,找不到返回 404。

修复:将 GET /ip-allowlist 路由移到 /{key} 之前(FastAPI 路由优先级)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 20:42:42 +08:00
Your Name ede7fbaa64 fix: settings PUT 后热更新内存 — 无需重启即生效
问题:修改 Telegram 等设置后需要重启服务器才能生效,
因为 load_settings_from_db() 只在启动时运行。

修复:PUT /api/settings/{key} 写入 DB 后,
同步更新 settings 内存对象(DB_OVERRIDE_MAP 映射 + INT_SETTINGS 类型转换)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 20:28:51 +08:00
Your Name 32feb1b6db fix: 全站 ruff 清零 — 77 errors → 0
Fixes:
- F821: install.py site_url 未定义(NameError)、sync_v2.py os 未导入、script_jobs.py LOG f-string
- F401: 移除 22 个未使用导入(models/__init__.py、install.py、auth.py 等)
- B904: 20 个 except 块 raise 添加 from e/from None
- S110: 20 个有意的 try-except-pass 添加 noqa 注释(SSH清理/DDL幂等/WS断连)
- B007: 3 个未使用循环变量重命名(dirs→_dirs、server_id→_server_id)
- F541: 3 个无占位符 f-string 修正
- F841: auth.py 未使用 ip_address 变量移除
- S105: auth_service.py Redis key prefix 误报 noqa
- B023: script_execution_flush.py 循环变量绑定修复

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 20:07:45 +08:00
Your Name 97be1fd214 完善审计日志中文化:auth_service/script_service/sync_engine 全部 detail 改中文 + 前端新增 webssh_token/refresh_token_mismatch 等映射 + MCP deploy 修复
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 02:57:51 +08:00
Your Name fd9b7d85b5 fix: 批量安装按钮检测 + 审计日志中文化 + terminal断开反馈
1. updateBatchBar() hasAgent 判断从 agent_api_key_set 改为 agent_version
2. 所有后端 AuditLog detail 字段统一中文
3. audit.html 操作/目标类型中文映射
4. terminal.html 断开覆盖层 + 重新连接按钮
5. Agent os_release 上报 + 前端系统版本拆分

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 02:17:35 +08:00
Your Name ccb78ed980 feat: SSH key preset system — dropdown + backend auto-resolve
- Add SshKeyPreset model + repo (encrypted_private_key, public_key)
- Add ssh_key_preset_id to Server model + schemas
- Add CRUD + reveal API routes (/api/ssh-key-presets/)
- servers.py: auto-resolve ssh_key_preset_id → decrypt → set ssh_key_private
- servers.html: key preset dropdown in key auth section, hides path/textarea on select
- Mirror password preset pattern: zero frontend credential exposure

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-25 22:49:57 +08:00
Your Name cdd0be328a fix: 六轮深度扫描 — 47项Bug修复、安全加固、死代码清理
Critical runtime bugs:
- terminal.html WebSSH完全不可用(URL前缀/JSON解析/Content-Type三处错误)
- servers.py路由遮蔽:/logs被/{id}拦截,3个前端页面同步日志查询失败
- scripts.html startExecPoll()→startExecPolling(),长任务快速执行崩溃
- agent.py {value!r!s:.50}格式串非法,agent发非数值时ValueError
- alerts.html d.daily.reduce()无null检查,API返回空数据时TypeError

Resource leak / stability:
- websocket.py僵尸连接未关闭TCP,文件描述符泄漏
- websocket.py _last_alert_time字典无限增长(加1小时过期清理)
- asyncssh_pool.py全忙时超过MAX_CONNECTIONS无限增长
- self_monitor.py Telegram告警无冷却,宕机时每30秒刷屏
- schedule_runner.py一次性调度执行超60秒会重复触发
- 限速脚本EXPIRE每次重置窗口可绕过(改用Lua原子脚本)

Security:
- JWT access token加token_version声明,改密码后旧token立失效(零宽限)
- INSTALL_MODE导入时常量→动态函数,安装后JWT认证不再残留禁用
- install.py /lock端点加管理员存在性验证,防止阻断安装
- ServerUpdate schema移除connectivity只读字段,防止伪造连接状态

Frontend fixes:
- doExec()缺r.ok检查、commands.html null检查
- _server_to_dict()补last_checked_at+ssh_key_public
- _field_match()逗号cron表达式修复
- alerts类型显示、SSH会话名称、搜索高亮定位
- 一次性/循环定时任务(run_mode+fire_at+自动禁用)

Dead code removed (400+ lines):
- SyncService batch_push/_push_single等5个方法(零调用者)
- 5个未使用schema(SyncCommands/SyncConfig/SyncSftp/FileDeploy/PaginatedResponse)
- 6个零调用service方法、3个无前端API端点
- 4个未使用import

Schema migrations:
- push_schedules: run_mode + fire_at列,cron_expr改NULL
- servers: 7个新列 + ssh_key_private/public VARCHAR(500)→TEXT

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-24 16:26:40 +08:00
Your Name 9bba58a529 feat: Telegram test+ChatID, Agent IP allowlist/upgrade, alert history page
Telegram:
- POST /api/settings/telegram/test: send test message (checks token+chat_id)
- GET /api/settings/telegram/chats: call getUpdates, return up to 5 chats
- settings.html: test button + detect chat IDs with click-to-fill

Agent IP allowlist (web/agent/agent.py):
- allowed_ips config: list of trusted IPs/CIDRs from config.json
- _ip_allowed(): checks exact IP, CIDR (ipaddress module), hostname
- verify_api_key(): now checks IP allowlist before API key
- /health: also checks IP allowlist
- install.sh: auto-extracts central server IP from --url, adds to allowed_ips

Agent auto-upgrade (server/api/servers.py):
- POST /api/servers/{id}/upgrade-agent: SSH → curl new agent.py → systemctl restart
- servers.html: 升级 Agent button in Agent tab (only when online)

Alert history (new page):
- domain/models: AlertLog table (server_id, type, value, is_recovery, created_at)
- migrations.py: CREATE TABLE IF NOT EXISTS alert_logs
- websocket.py: _save_alert_log() called from broadcast_alert/recovery
- settings.py: GET /api/alert-history/ (paginated, filters), GET /stats
- main.py: register alert_history_router
- alerts.html: new page with stats cards, top-servers bar chart, alert list
- layout.js: 🔔 告警中心 added to sidebar nav

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 18:27:12 +08:00
Your Name 488838c989 feat: separate subscription/manual IPs + master enable/disable toggle
Data model (separate storage):
- login_allowlist_enabled: 'true'/'false' master switch
- login_subscription_url: URL to fetch every 2h
- login_subscription_ips: last fetch result (replaced entirely each refresh)
- login_manual_ips: manually added (never overwritten by refresh)
- login_allowed_ips: combined (used by auth for legacy compat)

ip_allowlist_refresh.py:
- Saves to login_subscription_ips (replace) + rebuilds login_allowed_ips

auth_service.py:
- Login check: only runs when LOGIN_ALLOWLIST_ENABLED is 'true'
- Combines subscription_ips + manual_ips at auth time (no stale combined key needed)

settings.py:
- POST /ip-allowlist/toggle: quick enable/disable endpoint
- GET /ip-allowlist: returns subscription_ips, manual_ips separately
- POST /ip-allowlist: saves manual_ips + optional subscription_url + optional enabled

settings.html:
- Toggle switch: enable/disable with warning if list is empty
- Green notice when enabled (warns about self-lockout risk)
- Grey notice when disabled
- Subscription IPs section: read-only, auto-refresh badge
- Manual IPs section: deletable per-IP, clear all button

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 18:19:47 +08:00
Your Name da43960f38 feat: subscription URL stored in settings, auto-refresh allowlist every 2h
config.py:
- LOGIN_SUBSCRIPTION_URL: stored in DB, configurable from UI
- LOGIN_MANUAL_IPS: manually added IPs (preserved across auto-refresh)

background/ip_allowlist_refresh.py (new):
- ip_allowlist_refresh_loop(): primary-worker task, every 2h
- _do_refresh(): fetch subscription, parse IPs, merge with manual IPs,
  write combined to LOGIN_ALLOWED_IPS + DB setting; runs once at startup (5s delay)
- get_last_refresh_time(): returns last successful refresh timestamp

main.py:
- Register ip_allowlist_refresh_loop as background task

settings.py:
- GET /ip-allowlist: now returns subscription_url, manual_ips, last_refresh
- POST /ip-allowlist: saves manual_ips + subscription_url, triggers refresh
- POST /ip-allowlist/manual: append to manual list only, trigger rebuild
- DELETE /ip-allowlist/ip: remove single manual IP, trigger rebuild

settings.html:
- Subscription URL field with 保存 + 🔄 instant-refresh button
- Last refresh timestamp display
- Status badge shows '订阅自动刷新' when URL configured
- IP badges: blue=manual (deletable), grey=from subscription (auto)
- 预览 button to inspect subscription IPs without saving

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 18:15:29 +08:00