18 Commits

Author SHA1 Message Date
Gloridust
d29ea66730 docs(p0): 记录 QEMU OOM 的最终解决——改用原生 arm64 runner(v1.4.4 闭环) 2026-07-08 03:57:12 +08:00
Gloridust
9981e9a5dc feat(design): 设计系统文档 doc/dev/design.md + 无障碍基线(焦点环/动效降级)
- doc/dev/design.md:把牛奶布艺设计语言正式沉淀为规范——设计哲学、令牌表(色彩/圆角/
  阴影高光/动效曲线,含深浅色)、布局与三 Tab 信息架构、逐组件规范(卡片/按钮三级/输入框/
  徽标体系/分段 Tab/折叠菜单/空状态/Toast/进度/模态)、动效、无障碍、文案、反例、落地清单。
  令牌与 class 名与 styles.css 一一对应,是可执行的活规范而非抽象理论。
- 无障碍基线(此前为 0):全局 :focus-visible 绿色焦点环(仅键盘导航出现,鼠标不触发;
  无边框拟物 UI 尤其需要);prefers-reduced-motion 降级(去装饰动效、保留 spinner/进度的
  必要循环)。已用真实构建 CSS 截图验证焦点环不被按钮 overflow:hidden 裁剪、贴合胶囊圆角。
2026-07-08 03:13:09 +08:00
Gloridust
f3b3219cdb feat: 公网访问加固(登录限速/会话滑动续期)+ 管理页三 Tab 信息架构重构
公网/NAS 稳定性专项扫描的产出:

① 登录限速:面板常被直接暴露公网,/api/auth/login 原先无任何限速可被无限爆破。
双键计数(IP+用户名 5 次、IP 20 次 / 15 分钟窗口),触发回 429 + 面板日志告警;
用 socket 直连地址而非可伪造的 XFF;成功登录清零。已实测:5×401 → 第 6 次 429。

② 会话滑动续期:固定 30 天硬过期在"常年挂着"的 NAS 场景表现为"隔三差五要重登"。
现剩余不足一半自动续满(服务端),鉴权层顺手刷新 cookie maxAge(浏览器侧同步)——
活跃用户永不掉线,闲置超时才需重登。

③ DNS-rebind 钩子补 return reply(async 钩子已 send 后的规范终止写法)。

④ 运维文档新增「公网访问 / 反代部署」章节:nginx 完整示例(ws 升级头 +
proxy_read_timeout 3600s——默认 60s 是"桌面挂着不动 1 分钟就断线"的元凶 +
client_max_body_size)、Caddy/frp/Cloudflare 注意事项、PANEL_ALLOWED_HOSTS 提醒。

⑤ 管理页重构:7 个区块一条长滚动 → 「实例 / 用户 / 系统」三 Tab
(实例=日常操作;用户=子账号+本账号;系统=残留容器+数据卷+诊断日志+关于)。
牛奶布艺分段选择器(凹槽+浮起胶囊),Tab 角标点提示待处理事项(可升级/有残留),
记住上次停留 Tab;子账号(非管理员)视图不变。
2026-07-08 02:57:10 +08:00
Gloridust
fda9522b78 fix(critical): 实例镜像 arm64 构建 OOM + 版本耦合无兜底致面板锁死(v1.4.1 连环 P0)
v1.4.1 只发布了面板、实例镜像 CI 失败,叠加版本耦合无兜底 → 用户升级指示器消失、
实例卡在坏的 1.4.0、浏览器仍黑屏。两处修复:

① Dockerfile arm64 OOM:CI 的 QEMU arm64 下第二个 apt 层 OOM(两次 apt-get update +
snapshot 源被后续层反复拉取)。合并为单层一次 update+install、装完即移除 snapshot 源、
Acquire::Languages=none + Retries=3,砍掉内存/网络峰值。本机原生 arm64 构建 + 全套探针
(Chromium 149 / DPI 98304 / CEF 连跑两次 / 30s 零崩溃 / 窗口可见)已验证通过。

② 版本耦合安全兜底:面板偏好「与自身同版本」的实例镜像 tag,但该 tag 在本地和 registry
都不存在时(CI 部分失败/发布时序),原先会指向不存在的 tag → 检测恒空、升级拉不到。
新增 resolveInstanceImage():不可达则回退 :latest,功能永不因版本错配而瘫痪(已运行时验证
9.9.9→latest)。启动时与 ensureImage 前各解析一次。

P0 复盘记录连环成因;待办:改用原生 arm64 runner 替代 QEMU、matrix 失败联动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 12:11:16 +08:00
Gloridust
4d9cc1015a fix(critical): Chromium 版本锚定(snapshot) + CI 产物门禁 + 面板/实例版本耦合 + 声音重连修复
v1.4.0 三项反馈的修复:

① 浏览器实例仍黑屏(P0):CI 构建时 apt 拉到 Chromium 150(本地验证的是 149 缓存层)。
实测 150 在本栈上「非首次启动」路径(--no-first-run / First Run 哨兵 / 同 profile 第二次启动)
与 --start-maximized 均直接 SIGTRAP —— 首跑正常、之后必崩,无任何 flag 可绕。修复:
- Dockerfile 从 snapshot.debian.org 固定快照(20260704T180435Z)锚定安装 149.0.7827.196(R1);
  升级 chromium = 显式改快照+版本并过全套门禁。已验证:锚定镜像 DPI ✓、CEF 连跑两次 ✓、
  30s 零崩溃 ✓、窗口可见 ✓。
- release.yml 产物门禁:先构建 amd64 在 runner 上跑四项探针(DPI/CEF两次启动/崩溃循环/微信模式),
  全过才推送——「验证的东西和发布的东西必须是同一个 artifact」(v1.4.0 的流程教训)。
- 本地门禁 CEF 探针升级为连跑两次(单次恰好漏掉非首跑崩溃)。

② 面板/实例版本耦合(R1):正式版面板把实例镜像 :latest 改写为与自身相同的版本 tag,
同版本一起验证发布,杜绝「低版本面板 × 高版本实例」未验证组合;env 显式锁其它 tag 则尊重、
dev 面板保持 latest。配套:本地缺该版本镜像但远端存在时,升级引导也会亮起。

③ VNC 声音大概率无声:vncAudio 的 opened 标志断线不复位 → socket 重连后跳过 emit('open'),
kclient 永不重新推流 → 实例升级/重启/面板更新/网络抖动后全程静音。修:disconnect 时复位。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:55:08 +08:00
Gloridust
9ec3ea2d73 docs+fix: 稳定性设计文档(远程桌面/升级链路决策全记录)+ 重置设备ID 也走 keepImage
- doc/dev/稳定性设计.md:两条命脉链路的现行设计、每个决策的原因(含故意不做的事:
  单会话互踢、页内软重连、激进健康探测)、已知单点(KasmVNC 进程级卡死只能重启容器)。
- regenInstanceMachineId 补上 keepImage(恢复类操作幂等,R10 的最后一处漏网)。
- 发布门禁全项通过:DPI=98304、CEF 探针存活、30s 零崩溃、微信模式就绪、
  升级路径(keepImage 保版本 → 升级换 latest → LogConfig 20m×2 生效)、面板部署 0 错误。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:10:03 +08:00
Gloridust
7dc2053c0b fix(stability): 重启/自愈幂等(keepImage)+ 代理双侧 TCP keepalive —— 远程桌面稳定性审查
重大逻辑问题:「重启」「卡死自愈」「看门狗自愈」都走 runInstance 按本地 :latest 重建——
只要本地镜像被任何一次拉取更新过(新建实例/升级任一实例),下一次重启/自愈就把该实例
【隐式换镜像】。若本地新镜像恰好是坏的(如 v1.3.0 时期),一次看门狗自愈就能弄坏一个
从没点过升级的实例("我什么都没动它怎么坏了")。修:runInstance 增加 keepImage 选项,
重启/自愈沿用容器当前镜像重建(且跳过 ensureImage,离线也能重启);换镜像只走显式升级。
已实测:keepImage 重建后镜像不变;不带 keepImage(升级)换到 latest;新容器 LogConfig 生效。
守则新增 R10(重启必须幂等)。

代理加固:面板↔实例 双侧 ws 套接字启用 TCP keepalive(30s)。客户端断网/切网(WiFi→4G、
NAS 休眠)时 TCP 不会主动通知,半开死连接可挂数小时——对 KasmVNC 表现为幽灵会话占坑,
与新连接并存正是历史上"Xvnc 卡死需重启容器"的诱因之一;30s 探测让死连接分钟级回收。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:05:06 +08:00
Gloridust
7ec79314bb docs(dev): 架构守则 + 发布门禁 + P0复盘簿 —— 把历次 P0 的学费固化成硬约束
从 v1.2.6 自更新黑屏、v1.2.9~v1.3.1 DPI 单位事故、磁盘暴涨、升级卡死等真实 P0 中
蒸馏出 9 条架构不变式(R1~R9):版本锚定可回滚、声明式自重建、资源必有边界、
长任务异步化、面板×实例兼容矩阵、根因=解释全部症状+A/B实证、AI PR 审语义、
发布节奏(特性不当天发)、冒烟测用户动作。发布门禁给出逐条可执行的命令清单
(含微信 CEF 等价探针、升级路径测试);P0 复盘簿记录每次事故的流程性成因。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:37:48 +08:00
chenshu007
a032d17d71 feat: add optional docker-socket-proxy hardening overlay
Adds docker-compose.secure.yml and doc/安全加固.md as fully opt-in
security hardening for any WOC deployment.

The raw docker.sock mount in the panel is root-equivalent: a compromised
panel process can create privileged containers, mount the host filesystem,
or stop/delete arbitrary containers. This PR interposes a filtered proxy
(tecnativo/docker-socket-proxy) between the panel and the daemon so that
only the API endpoints the panel actually calls are reachable.

Changes:
- docker-compose.secure.yml: compose overlay that adds the proxy service,
  wires the panel to it via DOCKER_HOST, and shadows /var/run/docker.sock
  with /dev/null to block direct socket access at the filesystem level.
  Allowed: CONTAINERS, EXEC (required for app install / file ops / xdotool),
  IMAGES, VOLUMES, POST, INFO. Everything else is explicitly denied.
- doc/安全加固.md: explains the threat model, how the proxy mitigates it,
  residual gaps (endpoint-level filtering cannot inspect request bodies),
  usage instructions, and image digest pinning as a complementary practice.

Default docker-compose.yml is not modified.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 00:30:19 +08:00
Gloridust
ac7389e75f feat(ci/telegram): 发布通知自动置顶 + 取消上一个 release 的置顶
tg-notify.mjs:release/手动触发发完消息后,用 getChat 读群当前置顶(=上一个 release),
pinChatMessage 置顶新消息(静音,避免二次提醒)、unpinChatMessage 取消旧的——无需持久化存储。
issue 通知不置顶;置顶失败(机器人非管理员/无置顶权限)仅跳过、不影响通知本身。
文档补充:需把机器人设为群管理员并开启「置顶消息」权限。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:19:14 +08:00
Gloridust
1c34777353 feat(panel): 管理员数据卷管理(整卷备份/恢复 + 文件浏览器)
管理页 → 实例「管理」→ 数据卷(仅 admin)。解决大量用户"把 PC 微信数据迁移上 docker"的诉求。

- 整卷备份:流式打包 /config 为 .tar.gz 下载(大文件不入内存);恢复:上传覆盖回 /config。
  machine-id 存在卷内随包迁移 → 跨 woc 实例恢复可保留聊天记录。
- 文件浏览器:浏览/上传/上传并解压(.tar/.tar.gz)/下载/改名/移动/删除;PC 数据打包上传解压后重启实例。
- 全程在运行中的实例上操作(exec + docker cp,运行容器才可 exec);恢复为全量覆盖,强提示并建议重启。
- 安全:仅 admin;路径严格限制在 /config、禁止 .. 穿越;上传落地为 abc 属主。
- docker.ts 抽出 extractSingleFileFromTar 复用(PAX 头跳过),新增 list/mkdir/move/delete/upload/
  extract/download/backup(stream)/restore;index.ts 加 9 个 /volume 管理路由;前端 VolumeManager 弹窗
  + 线性 SVG 图标(替代渲染不一致的 emoji);新增 doc/数据卷管理.md。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 22:01:41 +08:00
Gloridust
54ed841a68 feat(admin/ci): 管理页折叠菜单 + 空状态/主页优化 + Telegram 命令机器人
管理页 UI/UX
- 实例卡片操作改为「管理」分类折叠菜单(默认收起,点开按 运维/设置/危险 分组的
  文字操作),替代之前难辨认的图标排;删除单独成组、红色,降低误点
- 修复展开一张卡片时同行其它卡片被 grid 拉等高(inst-grid align-items:start)
- 管理页空状态(无实例/无子账号)改为图标+标题+说明+引导按钮
- 主页实例卡片加副行(状态·微信版本)、悬停上浮高亮

Telegram 命令机器人(轮询版,纯 GitHub Actions,无服务器)
- .github/workflows/telegram-bot.yml + scripts/telegram-bot.mjs
- 私聊/群组命令:/help /releases /release <tag> /issues /issue <编号>
- cron 每 5 分钟 getUpdates,处理后用 offset 向 Telegram 确认,无需持久化存储
- 受 vars.TELEGRAM_BOT_ENABLED 开关;命令非实时(cron 限制),文档已说明

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:29:02 +08:00
Gloridust
a24977dd1f ci: Telegram notify on release / new issue (serverless via Actions)
New .github/workflows/telegram-notify.yml: on release published or issue
opened, send a message to a Telegram group via @WechatOnCloudBot. Runs on
GitHub Actions (no server). Gated on vars.TELEGRAM_CHAT_ID so unconfigured
forks skip safely. Arbitrary text passed via env (no script injection),
sent as plain text with --data-urlencode (no markdown parse breakage).
Setup documented in doc/发布到GHCR.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:45:03 +08:00
Gloridust
38846b6429 feat: 中文输入条 + 文件下载/另存修复 + 卡连接自愈 + CF/MAC/音频
中文输入(彻底改造,弃用脆弱的 VNC IME 拦截)
- 关闭 KasmVNC enable_ime:VNC 直接打字回归纯 keysym,英文/数字正常、不再损坏
- 新增底部「中文输入条」:面板真实 textarea 原生输入法 → POST /type → 容器内 xclip+xdotool
  粘贴进微信,可靠且与浏览器/输入法无关。flex 列布局(nav/画面/输入条三者并列不遮挡),
  牛奶布艺主题配色,可一键收起。

稳定性 / 自愈
- watchdog 新增响应性探测:实例 I/O/服务 stall(进程在、显示在线但读不出 VNC 文件、永远"正在
  连接桌面")时,连续 2 次无响应即自动重启自愈
- 前端 12s 未加载出来 → 「桌面无响应」+ 重新连接/重启,不再无限转圈
- PWA 新 SW 接管即自动重载一次,更新一刷即生效(修"改了仍看旧界面")

文件
- 下载:正确解析 tar、跳过 PAX 扩展头(中文名文件曾因此大小错误/损坏)
- 另存:每次启动确保 /config/Desktop 归 abc,修微信另存"保存失败"

安全 / 伪装
- Host 白名单支持 *.example.com 通配 + X-Forwarded-Host(修 CF 反代域名仍被拒)
- 设备伪装新增真实网卡 MAC(厂商 OUI,替代容器本地管理位 MAC)

音频:扬声器自动连接(首个手势激活)、焦点离开自动断、麦克风(HTTPS)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:33:46 +08:00
Gloridust
27fa9c2670 fix: machine mask 2026-06-06 02:15:16 +08:00
Gloridust
c37588617e ci: optional Docker Hub mirror in release workflow
- release.yml: dual-push to GHCR + Docker Hub when vars.DOCKERHUB_USERNAME
  is set; falls back to GHCR-only when unset (no behavior change for forks).
- .env.example: surface docker.io as a first-class WOC_IMAGE_PREFIX option.
- doc/发布到GHCR.md: document the one-time Variable + Secret setup and the
  prerequisite of pre-creating the public repos on hub.docker.com.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-04 18:18:21 +08:00
Gloridust
edf8c349c6 uodate doc 2026-06-02 20:15:16 +08:00
Gloridust
58a746d4cb update doc 2026-06-02 14:31:37 +08:00