Ethan Zou
57c38975e3
Merge pull request #78 from chenshu007/feat/docker-socket-proxy-hardening
...
feat: optional docker-socket-proxy hardening overlay (不改变默认行为)
2026-07-04 23:37:05 +08:00
Gloridust
64633a8147
fix: 会话持久化( #95 ) + 深色剪贴板可读( #96 ) + 粘贴图片到桌面( #91 ) + 拉取超时( #99 )
...
- #95 会话改落盘持久化(sessions.json,与 accounts.json 同目录)+ 时长可配置 WOC_SESSION_DAYS(默认30天),
cookie maxAge 同步。根因:旧版会话只在内存,面板重启/自更新/看门狗重建都会清空 → 每次都要重登。
- #96 .clip-area / .security-status 误用不存在的 var(--mf-trough) → 深色下退回硬编码亮灰、配浅色文字成"白底白字"。
改回 var(--trough)。
- #91 桌面打开时监听 paste:剪贴板含图片则上传到桌面为文件(RFB 剪贴板仅文本,图片走文件通道),应用里可取用。
- #99 pullImage 加"无进度"超时(WOC_PULL_STALL_MIN,默认5分钟):NAS 直连 docker.io 卡死时快速失败而非无限
hang 住创建请求/前端转圈。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.8
2026-07-04 23:30:11 +08:00
Gloridust
8e15556e6f
feat(panel): 子账号改用户名(#89)
...
管理页每个子账号新增「改名」:弹窗输入新用户名(3-20 位字母/数字/下划线,查重),
保存即改登录名;会话以 userId 为准,改名后保持登录、下次用新名登录。
- store.renameUser(查重 + 写入)
- POST /api/admin/users/:id/rename(管理员,格式校验)
- api.renameUser + Admin 的 RenameUser 弹窗
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.7
2026-06-26 10:25:05 +08:00
Gloridust
7e21aca7bb
fix(self-update): 修一键更新后实例落错网络致黑屏(502)的根因
...
根因:自更新重建面板时 buildCreateOpts 复刻了旧容器的 Hostname(=旧短ID)。新面板 os.hostname() 因此指向
【已删除的旧容器】,ensureNetwork 的 docker.getContainer(hostname()) 404 → 探测不到面板网络 → 返回 null →
此后新建/重启/升级/看门狗软重启的实例都以 NetworkMode=undefined 落到默认 bridge 网络,而反代按容器名
(http://woc-wx-xxx:3000 ) 跨网络解析不到 → 502「桌面服务暂不可用」黑屏。直接 docker compose pull 的用户
不受影响(compose 重建的面板 hostname 正确),与线上现象一致。
两处修复:
- self-update.ts:重建面板不再复刻旧 Hostname,让 docker 用新容器自身短 ID 作 hostname。
- docker.ts ensureNetwork:探测面板网络时,hostname 候选失败再按容器名(woc-panel)兜底查找,彻底不依赖
hostname 与容器 ID 一致。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.6
2026-06-25 19:14:09 +08:00
Gloridust
da87d1fce4
feat(diag): 诊断包补宿主级信号——内存/cgroup/安全选项/实例实时内存
...
便于定位 Ubuntu server 等宿主上的"黑屏/502/反复重启"是否是资源/宿主限制问题:
- system.txt 增:内存&Swap限制支持、cgroup 版本/驱动、存储驱动、SecurityOptions(apparmor/userns/seccomp);
面板实例配置(SHM、docker硬内存上限是否设、GPU)、内存自愈 soft/hard 阈值。
- 每实例增:运行中实例的当前内存占用 MiB(配合宿主总内存/OOMKilled 一眼判断是否内存不足)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.5
2026-06-25 01:37:20 +08:00
Gloridust
0fe35103ab
fix(proxy): 上游暂不可用回「自动重连」页,取代死的纯文本 502(修黑屏"桌面服务暂不可用")
...
实例在创建初始化/升级/重启/内存软重启、以及面板自更新(代理短暂中断)时会短暂 502;旧版回纯文本
"桌面服务暂时不可用",且 iframe 一旦载入它就 frameLoaded=true、不再触发重试,用户卡在黑屏死页。
现对浏览器导航请求回一个带 spinner、每 3s 自动重载的友好页:实例一就绪即自动连上;约 30s 仍不行
才转手动重试(20s 间隔重置计数以区分新一轮故障)。子资源(JS/CSS/XHR)仍回纯文本,不误喂 HTML。
诊断依据:两个 v1.2.4 用户的诊断包显示实例其实健康(KasmVNC 正常 accept),502 都出现在各类重启/初始化
的瞬时窗口,并非实例真的挂了。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-24 16:49:11 +08:00
Gloridust
60e53425e3
fix(audio): 点桌面即可解挂起出声 + 声音默认关每次打开都关
...
微信内声音播不出的根因:浏览器自动播放策略挂起 AudioContext 需用户手势恢复,但恢复手势监听绑在父窗口,
用户点的是同源 iframe 内的桌面画面、事件不冒泡到父窗口 → "点画面没用、得重开声音开关"才出声。
- VncAudio 加 resumePlayback(),Desktop 在 iframe 内补 pointerdown/keydown 手势监听转调它,点桌面即出声。
- 声音开关改为每次打开实例都默认【关】、不持久化 on(用户要求;蓝牙外放场景默认关最可预期)。
- 同步修正加载页文案(不再说"声音自动开启")。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.4
2026-06-23 21:50:51 +08:00
Gloridust
8bb721d3ba
fix(input): 转发模式回车改"补 Return 直接发送",不再切焦点回虚拟机( #81 )
...
上一版 #81 修复在发送后把焦点切回 VM,但转发模式下 VM 无 IME,用户接着打的拼音会以原始
keysym 直灌微信输入框(出现 "nniih'h你好啊" 串码)。改为打完文本后补一个 Return 直接发送,
焦点始终留在底部输入条,下一条仍用本机输入法安全输入。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-23 21:40:19 +08:00
Gloridust
c871a70b5a
fix: 安装用户未就绪重试( #74 ) + 关窗清理音频桥( #82 ) + 输入条发送后回焦点( #81 )
...
- #74 安装失败「unable to find user abc: no matching entries in passwd」:容器 init 未完成时
abc 用户还没建好,docker 创建 exec 即报 400。加 execCreate 重试包装(对该错误重试 ~12s),
超时给清晰中文提示,而非透传难懂的 docker 400。
- #82 开音频后关窗再进必须重启实例:React 清理在直接关窗时不一定执行,残留 audio socket.io
(开麦时还占着麦克风)与下次新连接并存把实例顶到"需重启"。加 pagehide(非 bfcache)即断开音频桥。
- #81 输入条回车只落字、还得点发送:发送后把键盘焦点交回 VM iframe,用户再按一次回车即在微信里发出。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-23 21:15:04 +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
ba32cc90ae
fix(dark): 撤掉不稳定的容器 portal 方案,浏览器深色回到 --force-dark-mode
...
实测极简 KasmVNC 容器里 portal 方案不可靠:autostart 起的 session dbus 会死
(gsettings 提交报 Connection refused),微信内核也不订阅 portal 的 color-scheme 信号,
所以微信不会变深、浏览器的"实时切换"也落空。故回到简单可靠的做法:
- 浏览器(Chromium):WOC_DARK=1 时 autostart 加 --force-dark-mode(开机即深,重启实例生效)。
- 微信:本开关无效,深色请在微信「设置→通用→深色模式」手动选。
- 移除:Dockerfile 的 xdg-desktop-portal/dconf 等包、autostart 的 portal 段、woc-dark.sh、
docker.ts 的 setInstanceDark、index.ts 的在线切换循环。面板开关仅持久化 desktopDark(驱动开机明暗)。
去掉每实例常驻的 dbus/portal,也消除其对稳定性的潜在影响。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.3
2026-06-19 02:11:47 +08:00
Gloridust
eefce1e9ba
fix(dark): woc-dark.sh 复用 autostart 真实总线 + 浏览器改纯 portal 跟随(修实时切换)
...
两个实测出的 bug:
1) 总线不一致:base 镜像里 XDG_RUNTIME_DIR=/config/.XDG,autostart 的 portal 总线在
/config/.XDG/woc-bus;但面板 docker exec woc-dark.sh 不继承会话环境,原先默认 /tmp/woc-run-<uid>,
连错总线 → 改了 dconf 也通知不到运行中的 portal → 微信/浏览器不实时变。改为 autostart 把真实总线
地址写入 /config/.woc-dark-env,woc-dark.sh source 它。
2) 浏览器恒深色:之前给 Chromium 加 --force-dark-mode 是启动期固定开关,切回浅色无效、必须重启。
去掉它,改为同微信一样纯靠 portal 的 color-scheme/gtk-theme,实现双向实时切换。
另:autostart 不再 export 静态 GTK_THEME(会让 GTK 恒深色),明暗统一走 gsettings。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-19 02:11:47 +08:00
Gloridust
698f767f82
fix(build): build-local 用 --provenance=false 出单一镜像,避免经典 API 仍解析到旧镜像
...
Docker 29 + containerd 镜像存储下,plain docker build -t :tag 会生成带 attestation 的
manifest list;docker image inspect / docker run / 面板用的 dockerode 解析 :tag 时不跟到
新 manifest list,仍指向同名旧镜像,导致重建实例还是用旧镜像。加 --provenance/--sbom=false
出单一镜像即可被经典 API 正确解析。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-19 02:11:47 +08:00
Gloridust
335b839e9a
feat(dark): 面板/实例统一深色开关(portal 实时切换)+ 修复 KasmVNC 致命崩溃自愈
...
实例深色不再靠 GTK_THEME(微信 Chromium 内核不吃),改用 freedesktop 桌面 portal:
每个实例常驻 session D-Bus + xdg-desktop-portal(gtk 后端) + dconf(固定总线地址),
面板顶栏主题开关(管理员)统一控制面板 + 实例深色,持久化进 accounts.json,并对运行中实例
docker exec woc-dark.sh 实时切换 color-scheme(微信据 SettingChanged 实时重绘),无需改 env。
- docker/Dockerfile: 装 xdg-desktop-portal(+gtk) / dconf / gsettings / dbus-x11(单独成层)
- docker/autostart: woc_setup_portal 常驻 portal,初始明暗由 WOC_DARK 决定
- docker/woc-dark.sh: 面板经 exec 调用,实时改 gsettings color-scheme
- store.ts: 持久化 desktopDark;docker.ts: envList 读它 + setInstanceDark;index.ts: /api/desktop-theme
- AppShell 主题开关接 setDesktopTheme(含 auto 跟随系统联动);移除 WOC_INSTANCE_DARK env
KasmVNC 客户端 bundle 长跑空闲后偶发 'lastActiveAt' 致命崩溃 → 仅在其致命错误浮层出现时
做一次干净整页重载(安全重连路径,非激进 churn),5min 限 4 次防死循环。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-19 02:11:47 +08:00
Gloridust
60dd6d018f
feat(panel): 深色模式(跟随系统/亮色/深色),主页顶栏图标切换
...
- 主页顶栏最右加主题图标按钮,循环切换 跟随系统 / 亮色 / 深色,存浏览器 localStorage。
- index.html 内联脚本首屏前置 data-theme,避免亮/暗闪烁。
- styles.css 加牛奶布艺·暗色变量(亮蓝黑 base、surface 更高一档、近纯黑阴影、极淡顶面高光),
data-theme=dark 强制深色、=auto 跟随 prefers-color-scheme、=light 维持亮色。
界面基于 CSS 变量,覆盖核心变量即整体适配。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-19 02:11:47 +08:00
Gloridust
bf99cc2116
fix(self-update): 防误报失败 + 防并发重入
...
- helper 重建前先等 2.5s,让面板把 HTTP 响应回给前端再停它,避免前端误报"更新失败"。
- triggerSelfUpdate 加 in-flight 守卫,两次点击不会派生两个 helper 互相打架。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-19 02:11:47 +08:00
Gloridust
f939538113
feat(panel): 面板内「一键更新面板」(自更新 + 失败回滚)
...
面板本身的更新此前必须去 docker/飞牛手动 pull + 重建。现支持在「关于」里一键更新(仅在检测到
新版时出现,管理员可见):
- 机制:面板先拉新镜像,再派生一个临时 helper 容器(用新镜像 + docker.sock)重建 woc-panel:
停旧 → 按旧容器配置(env 用「新镜像 baked + compose 注入」合并,保留 Labels/HostConfig/网络)
用新镜像重建 → 起来 → 稳定运行检查;失败则回滚旧镜像。helper 干完退出。
- 最坏情况(helper 也挂)用户 docker compose up -d 即可恢复(与手动一致)。
- 已本地实测:成功路径(重建后稳定、env/网络/label 保留)+ 回滚路径(坏镜像→回滚救活),
且全程不影响真 woc-panel。
- 新增 POST /api/admin/version/self-update + api.selfUpdatePanel + 「关于」按钮。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-19 02:11:47 +08:00
Gloridust
998a57ca25
fix(build): 加 panel/.dockerignore,禁止把宿主 node_modules 拷进镜像
...
Dockerfile 的 COPY web/ ./ 与 COPY server/ ./ 会把构建上下文里的 node_modules 一并拷入,
若宿主先前在本地装过依赖(如 macOS),其平台特定二进制(@esbuild/darwin-arm64)会覆盖镜像内
npm install 的 Linux 依赖 → tsx/esbuild 报 "Host version ... does not match binary version" 起不来。
加 .dockerignore 排除 **/node_modules(及 web/dist 等),保证本地构建与 CI(干净 checkout)一致。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-19 02:11:47 +08:00
Ethan Zou
9b8adb2c2a
Update README.md
2026-06-17 20:18:42 +08:00
Gloridust
23aa65d25c
fix(desktop): 回退到 1.1.7 的稳定连接行为,杜绝自动重连/重启 churn
...
回归分析(对比 v1.1.7,当时很稳):1.1.7 没有音频桥、没有 VNC 响应性看门狗、没有任何面板侧
自动重连/重启 —— 连接很稳,靠 noVNC 自带重连 + 手动刷新兜底。之后陆续加的东西反而制造了
"频繁卡死/重启":
- VNC 响应性看门狗自动重启 → 已默认关闭(f5e5bb5)。
- 本会话加的客户端自动重载(健康监测)+ 自动重启(heal)+ 回前台重载 → 本提交全部撤掉:
连接态监测改为「仅记录 [client] 日志、不自动动作」;恢复交给 noVNC 自带重连 + 用户手动
「重新连接/重启」(与 1.1.7 一致,不再有重载/重启 churn)。
- 音频桥(1.1.7 没有,是每实例一条额外 socket.io)→ 改为「声音」开关,默认关,回到 1.1.7 的
无音频连接行为;想听声音再开。「麦克风」开关仅在「声音」开启时出现。
诊断用的连接日志([vnc] 服务端 + [client] 前端状态)保留,便于继续定位。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.2
2026-06-17 02:51:11 +08:00
Gloridust
d33bba69b9
feat(desktop): 麦克风开关(默认关),不再一打开实例就抢占麦克风
...
之前打开实例 = 焦点即 getUserMedia 采集麦克风,导致 AirPods 等被切到低音质通话模式。
新增 nav「麦克风:开/关」开关,默认关:默认不占用麦克风(AirPods 保持高质输出),
需要语音/通话时再开。扬声器(听实例声音)始终可用、不受影响。状态存 localStorage。
- VncAudio 增加 micEnabled(默认 false)+ setMicEnabled();startMic 关闭时直接跳过 getUserMedia。
- Desktop 顶栏加开关,切换即时生效(setMicEnabled,不重建音频连接)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-16 00:25:36 +08:00
Gloridust
94e3400c93
feat: 客户端连接日志 + 自愈放宽预热窗口(避免误打断刚启动的实例)
...
诊断包实锤:刚创建/重启的实例 KasmVNC 需约 10-15s 预热,期间 nginx 已能回 101 但 ws 接收器
还没就绪(容器日志 0 条 got client connection)→ noVNC 拿到 ws 却收不到 RFB,反复 connecting→
掉线→重连。之前 15s 阈值会在预热期就整页重连/请求重启,反而拖长"正在连接"。
- 自愈阈值 15s→30s,先让预热 + noVNC 自带重连自行连上,不过早打断。
- 新增客户端连接日志(回传服务端,[client] 前缀,与 [vnc] 服务端日志对齐):
VNC 状态变化(connected/connecting/reconnecting/disconnected)、整页重连、请求重启、回前台重连、
iframe 加载 —— 下次卡死即可看清"到底是 noVNC 在重连、还是我们的逻辑在重载、还是服务端没起来"。
- 新增 POST /api/instances/:id/clientlog(需访问权,限长)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 22:55:44 +08:00
Gloridust
342a2f22a2
feat(panel): 记录每次远程桌面 ws 连接尝试/建立/失败/关闭
...
在面板反代层为每条 VNC ws 连接打点到实例持久日志:
- [vnc] 连接尝试 user/ip(server upgrade 处)
- [vnc] 上游已接受(101) · 桌面连接建立(proxyReqWs 收到上游 101)
- [vnc] 连接失败:<err>(proxy.ws 错误回调)
- [vnc] 连接关闭(持续 Ns)(client socket close)
与实例容器内 KasmVNC 的 "got client connection/accepted" 按时间对齐,即可精确定位卡死发生在
"面板→实例之间"(有尝试无 101)还是"实例 KasmVNC 内部"(有 101 但无 got client connection)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 22:31:34 +08:00
Gloridust
c0af1d766a
feat: 实例卡死自愈——客户端多次干净重连失败即请求重启容器
...
实测确认:KasmVNC 的 ws 接收器会偶发卡死(nginx 仍能serve 静态页让 noVNC 显示"正在连接",
但新 ws 永远 accept 不了;强刷新、重启面板均无效,只有重启实例容器能恢复)。这是 KasmVNC
基础镜像层的问题,面板无法直接修复,故改为可靠检测 + 自动重启兜底。
- 客户端健康监测(7d9b5ac,依赖可靠的 noVNC_connected class)干净重载 2 次仍连不上后,
调 POST /api/instances/:id/heal 请求重启该实例(数据保留),再整页重载连到全新容器。
客户端 + 服务端各 3 分钟限频,防重启风暴。
- heal 接口:需对该实例有访问权(让被卡住的用户能自助恢复,不必等管理员)。
- 放弃 /websockify 合成 WebSocket 探活:KasmVNC 握手挑剔(返回 404),且无法在健康态验证卡死探测,
改用客户端可靠的连接态信号驱动恢复。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 16:23:35 +08:00
Gloridust
7d9b5ac80d
feat(desktop): VNC 连接健康监测 + 卡住自动干净重连
...
读取 kasmweb 在同源 iframe <html> 上的连接态 class(noVNC_connected 等),iframe 已加载
但 VNC 持续 ~15s 不在 connected(卡在"正在连接"/noVNC 自动重连反复失败)→ 自动整页重载干净
重连,不再把用户晾在"正在连接…超时…无响应"。sessionStorage 限流:2 分钟内最多自动重连 2 次,
避免实例服务端真卡死时无限重载(那种需重启实例)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 16:09:29 +08:00
Gloridust
b0511f5f11
fix(desktop): 切实例不再重挂 iframe,根治"A→B→A 卡死(刷新无效)"
...
iframe 之前带 key={id},切换实例时 React 会重挂它(删旧元素+建新元素)。删旧元素时
VNC ws 未必干净关闭 → 实例服务端残留半开连接;回到该实例再开新 ws 时新旧并存把 Xvnc
卡死(服务端卡死,刷新都救不了,只能重启容器)。这正是"依次开 A、B,再切回 A 即超时"的根因,
且是长期存在的 bug。
- 去掉 iframe 的 key:切实例只改 src,浏览器“导航”iframe —— 旧文档 unload 干净关闭旧 ws,
再加载新实例,不留半开连接。重连仍走整页重载。
- 与 7fecc09(回前台干净重连)互补:分别覆盖“切实例”与“切走App/锁屏回来”两个并存触发点。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 15:36:42 +08:00
Gloridust
7fecc099bc
fix(desktop): 回到前台时干净重连,堵住 noVNC 自动重连的并存卡死
...
855997f 只把"手动"重连(重新连接/重启/切模式)改成整页重载,但没管 noVNC 自带的
reconnect=true 自动重连——切走 App / 锁屏挂起 ws 后回到前台,noVNC 会立刻开新 ws,
而服务端旧连接尚未释放 → 新旧 ws 并存把 Xvnc 卡死("开一段时间断联→卡死→重启"的主因,
手机最严重)。
- 新增 visibilitychange 监听:离开 ≥8s 后回到前台,直接整页重载干净重连(先彻底关旧 ws
再连),从机制上避开并存。阈值可按手机实测微调。
- 仍保留 noVNC reconnect=true 兜前台瞬时抖动(<8s 切回不重载)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 15:04:34 +08:00
Gloridust
855997fc70
fix(desktop): 重连/重启走整页重载,根治"重连后 Xvnc 卡死、第二次连接超时"
...
「桌面无响应」的「重新连接」按钮与「重启实例」后的重连此前用页内 bump vncNonce 重挂
iframe——而 setMode 的注释早已指出:页内重挂会让新旧两条 ws 短暂并存、概率性把实例的
Xvnc 卡死(需重启容器才恢复)。于是用户点「重新连接」反而把实例搞死,必须重启;重启后
第一次能连,再触发一次重连就又卡死、第二次连接超时。
- 「重新连接」按钮、restartInstance 重连统一改为 window.location.reload()(与 setMode 同,
整页卸载彻底关闭旧 ws 再干净重连,杜绝 ws 并存)。
- 退休已无用的 vncNonce 页内重挂机制(state/iframe key/effect 依赖一并清理)。
- 「桌面无响应」面板文案不再承诺"自动检测重启自愈"(响应性探测已默认关闭)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 14:22:29 +08:00
Gloridust
f5e5bb5bcb
fix(watchdog): VNC 响应性探测默认关闭,可用 env 重新开启
...
实测健康实例 VNC 探测 ~1ms 响应;但宿主级 CPU/IO 偶发争用(如同机重 docker build)
会让 8s 探测超时被误判为 stall,从而重启正常实例(用户反馈"浏览器经常重启")。
- 新增 WOC_WATCHDOG_HEALTH_FAILS(默认 0=关闭该探测);正整数 N = 连续 N 次无响应才重启。
- 内存阈值自愈(soft/hard)不受影响,照常工作。
- .env.example 补充说明。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
v1.2.1
2026-06-15 03:53:59 +08:00
Gloridust
0811d463e8
style(panel): 诊断/关于 改为扁平区块,修按钮断行与缺失的 chip 类
...
这两个区块各自只有单一内容,卡片外框显得厚重;按钮作为 flex 项被压窄导致
"导出诊断包(.tar.gz)" / "查看面板日志" 在按钮内断行。
- 去掉 inst-card 卡片外框,改为扁平 settings-block:标题下直接排描述/控件/操作。
- 主按钮 s-btn 固定内边距 + white-space:nowrap + flex:none,不再被压窄断行;
次要操作(查看面板日志 / 检查更新 / 发布日志)改 btn-text 文字链接,narrow 下自然换行。
- 修复:时间范围 chip 漏了 .chip 基类(只有 .chip-toggle 无内边距/圆角),补回 'chip chip-toggle'。
- 「导出诊断包(.tar.gz)」按钮文案精简为「导出诊断包」,格式说明移到脚注。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 01:33:20 +08:00
Gloridust
d42d401f89
chore(docker): Chromium 关闭后台 phone-home(--disable-background-networking)
...
Chromium 在受限网络(NAS/被墙)下后台不断尝试连 Google 推送(GCM)/组件更新/变体下载
并失败,刷屏 "gcm ConnectionHandler failed net error: -2" 等。加 --disable-background-networking
关掉这些后台流量。
仅影响后台 phone-home,不影响前台网页加载与真实网络错误提示(已验证 Chromium 正常启动、
前台浏览不受影响)。属安全的标准容器化 Chromium 用法。
注:日志里的 gcm/dbus 噪声本就无害,从来不是黑屏/重启的原因;此 flag 主要是减少受限网络下
的无谓后台请求。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 01:03:42 +08:00
Gloridust
9929a72be1
feat(panel): 全局日志系统 + 一键诊断包导出
...
单实例「日志」只记录该实例日志,无从排查跨实例/容器层面的问题(首个实例创建卡死、
打开实例黑屏不可用、升级失败等)。新增面板级全局日志 + 一键诊断包:
- logs.ts(新):统一持久化日志(面板数据卷,跨重建保留)。实例日志原语从 docker.ts
迁来;新增 appendPanelLog/readPanelLog、按时间裁剪 filterSince、一年保留 pruneOldLogs、
时间范围 24h/7d/30d/1y。无 docker 依赖避免循环。
- 仪表化:实例创建(含镜像拉取前后,定位首次拉取卡死)、删除、启停、重启、升级、
应用安装/更新、看门狗自愈 均写入面板全局日志(同时回显 stdout)。
- docker.ts buildDiagnostics:打包 system.txt(Docker/镜像/系统)+ panel.log +
instances/<id>.log(容器状态 inspect + 持久日志 + 实时日志)+ containers.txt(全部
woc-* 容器清单,含残留),手搓多文件 tar.gz(沿用既有无依赖 tar 风格)。
- 路由:GET /api/admin/diagnostics?range=(导出 tar.gz)、GET /api/admin/panel-log?range=;
启动 + 每 24h 跑 pruneOldLogs。
- 前端:管理页「诊断与日志」区——时间范围(24h默认/7d/30d/1y) + 导出诊断包 + 查看面板日志。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 00:48:36 +08:00
Gloridust
0abb5f8bdf
fix(panel): 开发版构建的版本展示,不再误标「已是最新」
...
本地/未发布构建(dev / dev-<sha>)无法与发布版做语义化比较,之前会错误显示
「· 已是最新」。现在:
- 「关于」卡:当前版本非 vX.Y.Z 时标「开发版」,仅把最新发布版作为信息展示
(当前版本 dev-xxx · 最新发布 v1.2.0),不显示「已是最新」、不触发红点。
- build-local.sh 默认烤入 dev-<短SHA>(而非裸 dev),便于辨识本地构建。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-15 00:35:30 +08:00
Gloridust
7293285d1a
feat(panel): 显示构建版本号 + 自动检测新版(Docker Hub/GHCR 红点)
...
管理界面此前看不到面板版本,也无从知道有没有新版可升。现在:
- 构建时把版本号烤进面板镜像:Dockerfile 新增 ARG/ENV WOC_VERSION(放末尾不
破坏依赖缓存);release.yml 用 git tag 注入(vX.Y.Z,手动触发为 dev-<sha>),
仅面板镜像消费;build-local.sh 支持 --build-arg(默认 dev)。
- 后端 version.ts:best-effort 查询 Docker Hub 与 GHCR 上 woc-panel 的语义化
标签取最大值,与当前版本比对;启动后 4s 首检 + 每 6h 复检 + 接口惰性触发,
失败静默(离线/被墙/私有源不报错、不显红点)。命名空间从 WOC_WECHAT_IMAGE 推断。
- 接口:GET /api/version(任意登录用户读缓存)、POST /api/admin/version/check
(管理员手动重查)。
- 前端:管理页「关于」卡显示当前版本/最新版/升级提示/检查更新/发布日志链接;
侧栏「管理」入口在有新版时点红点(仅管理员)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-14 22:40:17 +08:00
Gloridust
8cf15923a6
docs(readme): 写入浏览器实例支持 + 精简整理
...
v1.2.0 起云微是多应用平台(微信 + Chromium 浏览器),README 仍是纯微信
视角,补齐并整理:
- 新增「浏览器实例(登录网页版社媒)」章节:Chromium 随镜像就绪、登录态
写入数据卷常驻、多端共享、中文输入/文件/剪贴板共用一套;顶部导航加锚点。
- 标题/简介/一句话原理/核心特性/快速开始步骤/「面板能做什么」表 全部从
「微信」泛化为「实例(微信 / 浏览器)」;新建按钮文案对齐为「新建实例」。
- 精简冗杂:合并过长的「为什么默认 Docker Hub」blockquote 为一行。
- 资源占用补充浏览器实例量级说明;安全须知覆盖社媒账号;
路线图标记多应用平台完成、日志改为「持久化(跨容器重建保留)」。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-14 22:01:37 +08:00
Gloridust
2e201ff058
feat(panel): 持久化实例日志,跨容器重建保留
...
实例容器日志随重建(重启/升级/看门狗自愈)即丢失,用户看不到"上次为何
重启/崩溃"(浏览器实例常因内存触顶被看门狗重启)。现把生命周期事件 + 重启
原因 + 重建前的容器日志快照,追加到面板数据卷 /data/logs/<id>.log,跨重建保留。
- docker.ts: appendInstanceLog/readInstanceLog/snapshotContainerLog/deleteInstanceLog;
日志目录与 accounts.json 同卷(宿主 ./data-panel 持久化),单实例上限 ~400KB
超限截半保留最近;id 十六进制校验防路径注入。
- runInstance 删旧容器前先快照其最后日志、启动后记"容器已启动";
stopInstance 记"容器已停止";removeInstance 彻底删除时清理日志文件。
- 看门狗 recover() 写入自愈原因(hard/soft/health + 内存明细)。
- 日志接口返回「持久化历史 + 本次容器实时日志」两段。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-14 21:53:11 +08:00
Gloridust
167a80a0c3
feat(ci/telegram): issue 新回复也推送(简短)
...
telegram-notify 增订阅 issue_comment(created),tg-notify.mjs 加 issue_comment 分支:
💬 Issue #N 新回复 · 标题 / 评论人 / 评论摘要(≤400字) / 链接。PR 的评论也走此事件,按 C_PR 跳过;
回复通知不置顶(置顶仍仅限 release)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
v1.2.0
2026-06-14 21:25:14 +08:00
Gloridust
1c230316ed
feat(v1.2.0): 自定义实例图标——内置精选 + 上传裁剪
...
- AppIcon:内置精选平台图标(微信/Chromium/Telegram/小红书/抖音/B站/微博/知乎/YouTube/通用)+ ICON_CHOICES。
- 编辑器(管理菜单「图标」):选内置图标 / 上传图片用 react-easy-crop 方形裁剪→128px PNG / 恢复默认。
- 后端 setInstanceIcon + /api/admin/instances/:id/icon(仅 admin;icon=builtin:<key>/data:图片/空,限 ~225KB)。
- 新增依赖 react-easy-crop。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 19:41:40 +08:00
Gloridust
1ffc6d7e1d
feat(v1.2.0): 实例头像由首字母改为按应用类型的图标(自定义图标铺底)
...
新增 AppIcon.tsx(InstanceIcon + 内置 SVG 图标:微信/Chromium/Telegram/通用),侧栏与主页
卡片头像改用它(按 appType 出默认图标;data: 图片 / builtin:<key> 优先)。
新增 Instance.icon 字段 + publicInstance 下发,为「自定义图标(内置选择 + 上传裁剪)」铺底。
侧栏/主页标题「微信实例」→「实例」,主页副文案按应用名泛化。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 19:01:03 +08:00
Gloridust
7c9eb82a6f
fix(v1.2.0): 旧容器无 app-ctl.sh 时回退 wechat-ctl.sh(修微信实例误报未安装)
...
多应用分发对所有实例调 /woc/app-ctl.sh,但升级前的旧容器镜像里没有该脚本 → exec 失败 →
wechatStatus 兜底成"未安装",已装微信的老实例全变"待安装"。triggerWechat/wechatStatus 改为
bash -c:有 app-ctl.sh 则按 appType 分发,无则回退老的 wechat-ctl.sh(旧实例皆微信)。
老实例不升级也能正常显示/操作;升级到多应用镜像后自动走 app-ctl 分发。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 18:52:08 +08:00
Gloridust
b954f38c15
fix(v1.2.0): wechatStatus 改走 app-ctl <appType>(之前被 iCloud 回退)
...
b7fd778 提交时这行被本机 iCloud 回退成 wechat-ctl.sh status,导致非微信实例(Chromium)
状态去查微信二进制 → 永远「尚未安装」,且 Chromium 无安装按钮 → 卡死。改回 app-ctl.sh
<appType> status:Chromium 即报已就绪、可直接进入。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 18:46:42 +08:00
Gloridust
1da52bfb96
feat(v1.2.0): UI 按应用类型泛化(不再到处写死「微信」)
...
新增 APP_PROFILES(label/needsInstall/enterHint/updateLabel)+ appProfile()。
- 实例卡片:状态副文案、"进入实例"提示、安装/更新按钮均按 appType 显示;Chromium 已烤进镜像
(needsInstall=false)故不显示"下载安装/更新",状态直接"已就绪"。
- 桌面页:连接提示语用 enterHint(微信=扫码登录,Chromium=直接使用);安装中/未安装提示、iframe
标题、文件传输/剪贴板/输入条文案改为应用名或通用"应用/桌面"。
- 列表区"微信实例"→"实例"、空状态泛化。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 18:19:03 +08:00
Gloridust
179f5cf7a1
fix(v1.2.0): 重新落实选择器为 微信+Chromium(上个提交被 iCloud 回退)
...
eabedde 提交时 APP_OPTIONS 被本机 iCloud 冲突副本回退成旧版(telegram 可选、chromium 禁用)。
此处重新落实:微信(默认) + 浏览器(Chromium) 可用,自定义「即将支持」禁用,去掉 Telegram。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 17:47:30 +08:00
Gloridust
eabedde26b
feat(v1.2.0): 改为「微信 + Chromium 浏览器」,Telegram/自定义暂缓
...
Telegram 仅 x86_64,暂不做(代码留休眠)。聚焦多架构通用的 Chromium:
- Dockerfile apt 装 chromium(Debian Bookworm,amd64/arm64 均有;本地 arm64 实测装成、
chromium --version 正常,镜像 +~0.5GB)。Chromium 随镜像就绪,autostart 直接以
--no-sandbox 软件渲染拉起,无需「下载安装」。
- 新建实例选择器:微信(默认) + 浏览器(Chromium) 可用;自定义标「即将支持」禁用;去掉 Telegram。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 17:46:09 +08:00
Gloridust
b7fd778ab1
feat(v1.2.0): 多应用平台——创建实例时选 微信/Telegram(+Chromium/自定义占位)
...
镜像层(向后兼容,微信路径零改动):
- app-defs.sh:按 appType 给出 APP_BIN/APP_LAUNCH/APP_NAME(缺省回退微信)。
- app-ctl.sh:通用安装/状态分发;wechat 委托回 wechat-ctl.sh;telegram 下载官方 portable tar.xz。
- autostart:读 /config/.woc-app 选择启动哪个应用,读不到回退微信(老实例零改动)。
- 02-woc-app 钩子:把容器环境 WOC_APP_TYPE 落到 /config/.woc-app(缺则不写→回退微信)。
- Dockerfile:加 xz-utils(telegram 解压)+ COPY 新脚本。
后端:envList 透传 WOC_APP_TYPE(+自定义启动命令);triggerWechat/wechatStatus 改走
app-ctl.sh <appType>(微信行为不变);创建实例路由接受 appType。
前端:新建实例对话框加「应用类型」选择器(微信默认 / Telegram;Chromium、自定义标记"即将支持"禁用)。
本轮 Telegram(x86_64) 端到端可用;Chromium(待 apt 烤镜像) 与 自定义(待上传流) 下一轮。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 17:38:15 +08:00
Gloridust
11c5406d0f
Merge branch 'main' into feat/v1.2.0-multi-app
2026-06-14 17:19:41 +08:00
Gloridust
13ec0bdf8f
fix(desktop): 输入模式切换改为整页重载,修卡死 + 切换不生效
...
切换模式时原先 bump vncNonce 在页内重挂 iframe,会让新旧两条 VNC ws 短暂并存,
概率性把实例 Xvnc 卡死(需重启容器恢复、面板重启无效),且新连接常读不到新 enable_ime
(仍是英文)。改为 window.location.reload():先卸载旧页彻底关旧 ws,再以新模式干净重连,
正是用户实测唯一可靠的方式。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
v1.1.11
2026-06-14 17:08:00 +08:00
Gloridust
659d26ce68
fix(docker): typeInInstance 卡 ~2s 根因——xclip 未重定向 fd
...
xclip -i 会 daemon 化常驻持有剪贴板选区,并继承 docker exec 的 stdout/stderr,
导致 exec 要等这俩 fd 关闭、每次中文转发卡 ~2.1s。给 xclip 重定向 >/dev/null 2>&1
后整条链路降到 ~0.08s(实测 26× 提速)。中文输入条与无感模式都受益。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 16:57:33 +08:00
Gloridust
dfaa64ad39
fix(desktop): 修无感输入两处 bug(切换不生效 + "你好y呀"丢字)
...
1) 切到无感后当前会话仍是旧 enable_ime → 表现"还是打英文,要换页才行"。
setMode 现同步写 enable_ime 并 bump vncNonce 重挂 iframe,让 noVNC 立即按新模式重连。
2) "你好[空格]呀"打出"你好y呀":有序队列在中文转发期间会把下一个词的拼音首字母(y)当字面字符抢走。
改为队列活跃时只接管【数字】(原"混数字丢字"的祸首) + 回车/退格;字母绝不接管,交给输入法合成。
附带消除了之前"每个键都走 xdotool"导致的卡顿(字母回到原生合成快路径)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 16:48:59 +08:00
Gloridust
2edba455dc
feat(v1.2.0): 实例 appType 数据模型地基(向后兼容)
...
引入 AppType(wechat/telegram/chromium/custom)+ APP_LABELS + instanceAppType() 兜底。
Instance 增 appType?(可选) 与 customLaunch?;createInstance 接受 appType(默认 wechat)、
按应用取默认名;publicInstance 下发 appType(老实例无字段→回退 wechat)。
纯增量、不改现有运行行为;为后续 autostart 分发 / 各应用安装 / 前端选择器铺底。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-06-14 15:37:53 +08:00