diff --git a/README.md b/README.md
index 62cfe84..1bf50c8 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@
快速开始 ·
核心特性 ·
- 运行原理 ·
+ 运行原理 ·
安全须知 ·
技术方案
@@ -33,7 +33,7 @@
在飞牛 NAS(x86_64 / arm64)或任意 Docker 主机上运行服务端微信:可管理**多个**微信实例,每个实例是一个独立的微信会话;多个 web 用户通过浏览器访问被授权的实例,实现跨设备消息同步、多端共享。**不修改微信客户端。**
-> 设计与选型详见 [技术方案.md](doc/技术方案.md)。不熟悉 Docker?直接看 [Docker 运行模式详解](#docker-运行模式详解新手向)。
+**一句话原理**:每个微信实例 = 一个容器,里面跑 Xvfb 虚拟显示 + 官方原版微信,KasmVNC 把画面串到浏览器;同一实例被多个浏览器连 = 共享同一个微信会话。前面一层自研**面板**是唯一对外入口,经 docker.sock 按需创建/销毁实例并反向代理。
---
@@ -44,7 +44,7 @@
- 🖥️ **微信 PC 式界面** — 左侧实例栏 + 右侧内嵌桌面,侧栏可折叠,移动端自动转抽屉。
- 📦 **微信本体运行时下载** — 镜像不打包微信,面板一键「下载安装 / 更新」带进度条;按 CPU 架构自动取包。
- 🔁 **实例生命周期** — 启动 / 停止 / 重启 / 升级(拉新镜像重建、保留聊天记录),均在面板内一键完成。
-- 📎 **文件传输** — 原生拖拽上传 + 下载 + 删除,直达微信桌面 `~/Desktop`。
+- 📎 **文件传输 + 文本剪贴板** — 拖拽上传 + 下载 + 删除,直达微信桌面 `~/Desktop`;文本可经剪贴板中转送进微信(局域网 http 下也可用)。
- 🧩 **多端协作软锁** — 同一实例多人操作时自动只读 + 申请接管,避免键鼠打架。
- 🔒 **安全优先** — 面板为唯一入口,KasmVNC 凭据服务端注入、永不下发前端;docker.sock 仅管理员可触达。
- 📱 **PWA** — iOS「添加到主屏幕」、桌面 Chrome「安装」当原生 App。
@@ -52,110 +52,20 @@
---
-## 工作原理(一句话)
+## 文档
-每个微信实例 = 一个容器,里面跑 Xvfb 虚拟显示 + 官方原版微信,KasmVNC 把画面串到浏览器。同一实例被多个浏览器连 = 共享同一个微信会话。**不修改微信客户端**。
-
-前面一层自研 **面板(panel)** 是唯一对外入口:负责账号登录、子账号与**实例权限**管理,经 docker 引擎**按需创建/销毁**微信实例容器,并反向代理到对应实例——浏览器只和面板打交道,KasmVNC 的凭据由面板在服务端注入,不下发前端。
-
-```
-浏览器 ──▶ panel(:36080) ──┬─ / 面板 SPA(登录 / 实例网格 / 子账号 / 进入桌面)
- cookie 鉴权 ├─ /api/* 账号、实例、权限接口
- └─ /desktop/:id/* 反代 → 对应实例 KasmVNC(注入 Basic 鉴权)
-
-panel ──(docker.sock)──▶ docker 引擎 ──▶ 按需创建/销毁微信实例容器 woc-wx-
- 每个实例 = 独立容器 + 独立数据卷 + 独立微信会话
- 实例只在 docker 网络内暴露,不直连宿主
-```
-
----
-
-## Docker 运行模式详解(新手向)
-
-如果你对 Docker 不熟,这一节把本项目「怎么跑起来的」讲透。读完你就能看懂上面的图。
-
-### 0. 先认识 5 个 Docker 概念
-
-| 概念 | 一句话理解 | 在本项目里是什么 |
-|------|-----------|------------------|
-| **镜像 Image** | 只读的「软件安装包」,里面装好了程序和依赖 | `woc-panel`(面板)、`wechat-on-cloud`(微信实例) |
-| **容器 Container** | 镜像「运行起来」的实例,相当于「正在跑的程序」。一个镜像能跑出多个容器 | `woc-panel` 容器、多个 `woc-wx-` 容器 |
-| **卷 Volume** | 容器之外的持久磁盘。容器删了,卷里的数据还在 | 每个微信实例一个卷 `woc-data-`,存登录态和消息 |
-| **网络 Network** | 容器之间互通的「虚拟局域网」,容器之间可用**容器名**当域名互访 | 面板和所有实例在同一网络里,面板用 `http://woc-wx-:3000` 找实例 |
-| **docker.sock** | Docker 引擎的「遥控器」(宿主上的一个特殊文件 `/var/run/docker.sock`)。谁拿到它,谁就能指挥 Docker 创建/删除容器 | 挂进面板容器,面板才能「动态造微信实例」 |
-
-> **Compose** 则是「用一个 `docker-compose.yml` 文件描述要跑哪些容器,`docker compose up -d` 一条命令拉起」。本项目的 compose 里**只有面板一个服务**。
-
-### 1. 本项目有两类容器(运行角色不同)
-
-这是本项目最容易迷惑的地方:**不是所有容器都写在 `docker-compose.yml` 里。**
-
-| | ① 面板容器 | ② 微信实例容器 |
-|---|-----------|---------------|
-| 容器名 | `woc-panel`(固定一个) | `woc-wx-<随机id>`(可有多个) |
-| 用哪个镜像 | `woc-panel` | `wechat-on-cloud` |
-| 谁来启动 | **你** 执行 `docker compose up -d` | **面板**:你在网页点「新建微信实例」时,面板通过 docker.sock 自动 `docker run` |
-| 写在 compose 里吗 | 是 | **否**(运行期动态创建,compose 里看不到) |
-| 对外暴露端口 | 是,宿主 `36080` → 容器 `8080` | 否,只在 docker 网络内,由面板反代 |
-| 数据存哪 | 宿主目录 `./data-panel` | 各自的命名卷 `woc-data-` |
-| 生命周期 | 常驻 | 你在面板「删除实例」时销毁(默认保留卷) |
-
-一句话:**你只手动管面板这一个容器;微信实例是面板帮你按需开关的。** 这就是为什么面板要挂 `docker.sock`——它需要「遥控」Docker 去开关微信实例容器。
-
-### 2. 镜像从哪来:两种「构建/获取」模式
-
-容器要跑,先得有镜像。本项目的两个镜像有两条获取途径,**任选其一**([快速开始](#快速开始)对应方式 A / B):
-
-| | 方式 A · 本地自构建 | 方式 B · 拉取官方镜像 |
-|---|--------------------|----------------------|
-| 怎么做 | `./scripts/build-local.sh`(用本仓库 Dockerfile 在你机器上造镜像) | `docker compose up -d`(自动从 GHCR 下载现成镜像) |
-| 适合谁 | 官方还没发布镜像时 / 想自己改代码 / 内网无法访问 GHCR | 普通用户,开箱即用 |
-| 前提 | 本机能拉到基础镜像(node、KasmVNC base) | GHCR 上已发布且包为公开(见[发布到 GHCR](#发布到-ghcr)) |
-| 产物 | 本地镜像,标签和 compose 里写的一模一样 | 同名镜像,来自云端 |
-
-> compose 的拉取策略是默认值(`missing`):**本地已有同名镜像就直接用,没有才去 GHCR 拉**。所以方式 A 构建完,`docker compose up -d` 会直接用你的本地镜像,不会再联网。想升级到 GHCR 最新版:`docker compose pull && docker compose up -d`。
-
-> 第三个「镜像」其实是**微信本体**:它**不打进任何镜像**,而是你在面板点「下载并安装」时,由实例容器实时从腾讯官方 CDN 下到自己的卷里(见[数据持久化](#数据持久化))。
-
-### 3. 从零到能用,整体发生了什么
-
-```
-你: docker compose up -d
- └─▶ Docker 读取 docker-compose.yml
- └─▶ 拉起【面板容器 woc-panel】,挂上 ./data-panel 和 docker.sock,暴露 36080 端口
-
-你: 浏览器开 http://NAS:36080 → 登录 → 点「新建微信实例」
- └─▶ 面板通过 docker.sock 指挥 Docker:
- ├─ docker run 一个【微信实例容器 woc-wx-xxx】
- ├─ 给它挂一个新卷 woc-data-xxx(存登录态/消息)
- └─ 接到同一个 docker 网络(面板才能反代到它)
-
-你: 进入该实例 → 点「下载并安装」
- └─▶ 面板 docker exec 进实例容器,触发脚本从腾讯 CDN 下载微信、解压到卷
-
-你: 点「进入电脑版微信」→ 手机扫码
- └─▶ 浏览器 ⇄ 面板(反代+注入鉴权) ⇄ 实例容器的 KasmVNC ⇄ 微信窗口
-```
-
-### 4. 常用命令速查
-
-```bash
-docker compose up -d # 启动面板(首次会拉/用镜像)
-docker compose down # 停止并删除面板容器(不动数据卷和微信实例)
-docker compose pull # 把面板/微信镜像更新到 GHCR 最新
-docker ps # 看正在运行的容器(能看到 woc-panel 和各 woc-wx-*)
-docker logs -f woc-panel # 看面板日志
-docker logs -f woc-wx- # 看某个微信实例日志
-docker volume ls | grep woc # 看所有微信实例的数据卷
-```
-
-> ⚠️ 微信实例容器请**始终在面板网页里增删**,不要手动 `docker rm` 它们——否则面板的实例登记和真实容器会对不上。
+| 文档 | 内容 |
+|------|------|
+| [运行原理与 Docker 指南](doc/运行原理.md) | 工作原理 + 架构图;面向 Docker 新手的逐步拆解、常用命令、架构自动适配 |
+| [部署与运维](doc/部署与运维.md) | 数据持久化、常见问题排查、忘记超管密码的离线找回、目录结构 |
+| [发布到 GHCR](doc/发布到GHCR.md) | 用 GitHub Actions 或本机 buildx 把镜像发布到 GHCR |
+| [技术方案](doc/技术方案.md) | 完整设计文档与选型权衡 |
---
## 快速开始
-> 需已安装 Docker(含 Compose 插件)。x86_64 / arm64 均可。
+> 需已安装 Docker(含 Compose 插件)。x86_64 / arm64 均可。不熟悉 Docker?先读 [运行原理与 Docker 指南](doc/运行原理.md)。
`docker-compose.yml` 引用的是 GHCR 上的镜像 `ghcr.io/gloridust/{woc-panel,wechat-on-cloud}`。
**这两个镜像需先存在**——要么官方已发布(你能直接拉取),要么你在本地自行构建。二选一:
@@ -179,7 +89,7 @@ cp .env.example .env # 至少改掉默认密码 WOC_PASSWORD
docker compose up -d # 直接从 GHCR 拉取
```
-> 报错 `error from registry: denied`?说明 GHCR 上还没有该镜像(或包是私有的)。用方式 A 本地构建,或见下方[「发布到 GHCR」](#发布到-ghcr)。
+> 报错 `error from registry: denied`?说明 GHCR 上还没有该镜像(或包是私有的)。用方式 A 本地构建,或见 [发布到 GHCR](doc/发布到GHCR.md)。
无论哪种方式,都会拉起面板容器 `woc-panel`(唯一对外服务)。浏览器访问 `http://:36080`:
@@ -190,7 +100,7 @@ docker compose up -d # 直接从 GHCR 拉取
之后被授权的用户换任意设备打开同一地址登录面板,看到自己有权访问的实例,进入即是**同一个**微信会话。
-> 宿主只对外暴露面板的 `36080` 一个端口;微信实例容器仅在 docker 网络内、由面板反代,不直连宿主。要改端口/版本见 `.env`。
+> 宿主只对外暴露面板的 `36080` 一个端口;微信实例容器仅在 docker 网络内、由面板反代,不直连宿主。要改端口/版本/账号见 `.env`(可配置项见 [.env.example](.env.example))。镜像会按 CPU 架构自动适配([详见文档](doc/运行原理.md#架构自动适配))。
### 面板能做什么
@@ -200,96 +110,13 @@ docker compose up -d # 直接从 GHCR 拉取
| 实例权限分配 | 管理员 | 在实例上改「可访问账户」,或在账户上改「可访问实例」,双向管理 |
| 下载并安装 / 更新微信 | 管理员 | 对某实例一键下载官方微信 Linux 版到其数据卷、解压安装;带进度条;后续可一键「更新到最新版」 |
| 进入电脑版微信 | 被授权用户 | 在浏览器里操作对应实例的微信,扫码登录、收发消息 |
+| 文件 / 文本传输 | 被授权用户 | 拖拽上传 / 下载文件;文本经剪贴板中转送进微信 |
| 修改密码 | 所有人 | 改自己的登录密码 |
| 子账号管理 | 管理员 | 创建 / 禁用 / 重置 / 删除子账号,并分配实例访问权限 |
| 安装为 App | 所有人 | iOS Safari「添加到主屏幕」、桌面 Chrome「安装」当原生 App(PWA) |
> 子账号是**访问这套面板的身份**,不是另开一个微信。管理员隐式拥有全部实例访问权;子账号只能看到被授权的实例。
-
-> 微信本体**不打进镜像**,而是新建实例后在面板点「下载并安装」时下载到该实例的数据卷,所以镜像很小、构建快、不依赖腾讯 CDN。
-
-### 架构自动适配
-
-镜像本身多架构(amd64/arm64);下载微信时容器内**运行时再自动检测 CPU 架构**(`dpkg --print-architecture`)取对应官方包:
-
-| 运行机器 | 架构 | 自动下载 |
-|----------|------|----------|
-| Intel/AMD NAS、x86 服务器 | amd64 | `WeChatLinux_x86_64.deb` |
-| ARM NAS、Apple Silicon Mac | arm64 | `WeChatLinux_arm64.deb` |
-
-到飞牛上(无论 x64 还是 arm)`docker compose up -d` 同一条命令,无需改任何架构相关配置。
-
-### 自定义配置(可选)
-
-复制 `.env.example` 为 `.env` 后按需修改,可配置项见 [.env.example](.env.example):管理员账号密码、镜像版本(`WOC_VERSION`,建议上线后钉到具体版本)、PUID/PGID、时区、端口。
-
----
-
-## 发布到 GHCR
-
-两种方式任选其一。
-
-### 方式 A · GitHub Actions(推荐)
-
-仓库自带 GitHub Actions([.github/workflows/release.yml](.github/workflows/release.yml)),在你**推送 `vX.Y.Z` 标签或发布 Release** 时,自动构建多架构(amd64+arm64)镜像并推到 GHCR:
-
-```bash
-git tag v1.0.0
-git push origin v1.0.0 # 触发 Actions,产出 ghcr.io//woc-panel:1.0.0 等标签
-# 或在 GitHub 上 Publish 一个 Release(会额外打 latest):
-gh release create v1.0.0 --title v1.0.0 --notes "..."
-```
-
-> 注意:单纯 push tag 只产出 `X.Y.Z / X.Y / X`,**不会更新 `latest`**;要更新 `latest` 请改用 **发布 Release** 或在 Actions 里手动 `workflow_dispatch`。
-
-### 方式 B · 本机 buildx 手动构建并推送(不走 Actions)
-
-适合想立刻出包、或不依赖 CI 的场景。需要 Docker Buildx(Docker Desktop 自带;纯 Linux 跨架构需先装 QEMU:`docker run --privileged --rm tonistiigi/binfmt --install all`)。
-
-```bash
-# 1) 登录 GHCR(PAT 需 write:packages 权限)
-echo | docker login ghcr.io -u --password-stdin
-
-# 2) 首次创建并启用多架构构建器(已建过改用 docker buildx use woc)
-docker buildx create --name woc --use
-
-# 3) 构建并推送两个镜像(amd64 + arm64)。VER 与 git tag 保持一致(不带 v)
-VER=1.0.1
-docker buildx build --platform linux/amd64,linux/arm64 \
- -t ghcr.io/gloridust/woc-panel:$VER -t ghcr.io/gloridust/woc-panel:latest \
- --push ./panel
-docker buildx build --platform linux/amd64,linux/arm64 \
- -t ghcr.io/gloridust/wechat-on-cloud:$VER -t ghcr.io/gloridust/wechat-on-cloud:latest \
- --push ./docker
-```
-
-> 把 `gloridust` 换成你的 GHCR 命名空间(与 `docker-compose.yml` / `WOC_IMAGE_PREFIX` 一致)。
-> 只想本机自用、不推 GHCR,用 [`./scripts/build-local.sh`](scripts/build-local.sh) 构建本机架构单架构镜像即可。
-
-### 发布后:把包设为公开
-
-首次发布后还需把 GHCR 包设为公开,否则别人 `docker compose up -d` 会报 `denied`:
-
-1. 打开 GitHub → 你的头像 → **Packages** → 分别进入 `woc-panel`、`wechat-on-cloud`;
-2. **Package settings → Change visibility → Public**。
-
-> 若想保持私有,则使用者需先 `docker login ghcr.io`(用具备 `read:packages` 的 PAT)才能拉取。
-> 在镜像发布之前,本地用 `./scripts/build-local.sh` 自构建即可,无需等待发布。
-
----
-
-## 数据持久化
-
-- **面板数据**(用户、实例元信息、密码哈希):容器内 `/data`,映射到宿主 `./data-panel`。
-- **每个微信实例**:独立的 docker 命名卷 `woc-data-`,挂到该实例容器的 `/config`(微信本体在 `/config/wechat`,登录态与消息缓存在 `/config` 其余位置)。
-
-要点:
-- 删除实例**默认保留**其数据卷,下次同名重建可复用;只有显式勾选「彻底清除」才会删卷。
-- 备份某实例 = 备份对应的 `woc-data-` 卷(`docker volume` 系列命令)。
-- 卷需支持执行权限(微信本体直接从卷里运行);放在 `noexec` 卷上微信将无法启动。
-- 备份面板 = 备份 `./data-panel`。
-
-> **从旧版(单微信容器 + `./data` 绑定挂载)迁移**:旧形态把微信数据放在宿主 `./data`。新版用 docker 命名卷,结构不同,无自动迁移。如需保留旧会话,最简单是新建一个实例、重新扫码登录;或手动把旧 `./data` 内容拷进新实例的 `woc-data-` 卷。
+> 微信本体**不打进镜像**,而是新建实例后在面板点「下载并安装」时下载到该实例的数据卷,所以镜像很小、构建快。
---
@@ -297,7 +124,7 @@ docker buildx build --platform linux/amd64,linux/arm64 \
> ⚠️ **这套系统暴露的是已登录的微信,请务必认真阅读本节。**
-这套系统暴露的是**已登录的微信**——能登录面板的人就能看聊天记录、以你身份发消息。**面板还挂载了宿主的 `docker.sock`**(创建/销毁实例所需),它等同宿主 root 权限。因此:
+能登录面板的人就能看聊天记录、以你身份发消息。**面板还挂载了宿主的 `docker.sock`**(创建/销毁实例所需),它等同宿主 root 权限。因此:
- **绝不要把面板裸暴露公网**:只在内网访问,或经飞牛远程访问 / VPN / 内网穿透;
- 务必改掉默认密码(默认 admin / wechat):`cp .env.example .env` 后改 `WOC_PASSWORD`,或登录后在「修改密码」里改;
@@ -310,84 +137,11 @@ docker buildx build --platform linux/amd64,linux/arm64 \
## 中文输入
-**用你本地(客户端)的输入法打中文,容器内无需安装任何 IME。** 镜像已默认开启 KasmVNC 的
-「IME Input Mode」:拼音联想在你本机完成,只把成品汉字发进容器。直接在微信输入框打字即可。
+**用你本地(客户端)的输入法打中文,容器内无需安装任何 IME。** 镜像默认开启 KasmVNC 的「IME Input Mode」,并对 noVNC 的 IME 合成逻辑做了修复——**只在输入法「上屏」那一刻把成品汉字整串发进容器**,规避了原生实现逐字符差分带来的丢字 / 卡顿。直接在微信输入框打字即可。
- 默认值只对**未存过该设置的浏览器**生效。之前手动开/关过的,浏览器 localStorage 值优先;想验证默认效果用无痕窗口。
-- 已知小毛病:超长拼音串未全部转成汉字就回车,偶尔丢字([issue #97](https://github.com/linuxserver/docker-baseimage-kasmvnc/issues/97)),长句分段输入即可。
-- 兜底:Chrome/Edge 下本地 `⌘C` → 远端 `Ctrl+V` 无缝粘贴;Firefox 用控制面板的 Clipboard 文本框中转。
-
-## 常见问题
-
-| 现象 | 排查 |
-|------|------|
-| 新建实例失败 | 多为面板拉不到微信镜像或连不上 docker.sock。确认 `docker.sock` 已挂载、宿主能访问 GHCR;看面板日志 `docker logs woc-panel` |
-| 界面/消息显示成方块 | 中文字体没装好,确认实例镜像含 `fonts-noto-cjk` |
-| 微信起不来 / 黑屏 | 看实例日志 `docker logs woc-wx-`;确认 `seccomp=unconfined` 与 `shm_size` 生效。微信 deb 漏声明的运行时依赖已在 Dockerfile 内置 |
-| 排查缺哪个库 | `docker exec woc-wx- ldd /config/wechat/opt/wechat/wechat`,看 `not found` 项补进 Dockerfile 依赖层 |
-| 多人同时操作很乱 | 已内置「操作控制权」软锁:当前操作者每数秒心跳续约,其余端自动转为**只读遮罩**(仍可看画面),空闲超时(约 10s)自动释放,他人可点「申请控制」接管。仍建议同一时刻一人操作 |
-| 过段时间掉登录 | 微信桌面会话会定期失效,需手机重新扫码(见技术方案 6.2) |
-| 下载 / 更新微信失败 | 腾讯 CDN 偶发波动,重新点「下载并安装 / 更新」即可;脚本已内置主/备 CDN 自动回退 |
-| 架构不支持报错 | 微信仅提供 x86_64 / arm64;其他架构下载时会在面板状态里报错 |
-| 忘记超管密码 | 见下方「重置超管密码」离线找回 |
-
-查看面板日志:`docker logs -f woc-panel`;查看某实例日志:`docker logs -f woc-wx-`(实例 ID 可在面板看到,或 `docker ps | grep woc-wx`)。
-
-### 重置超管密码(离线找回)
-
-管理员密码无法被他人重置,忘记时按以下步骤离线找回:
-
-```bash
-docker compose stop panel # 1) 先停面板,避免覆盖你的手动修改
-```
-
-2) 编辑 `./data-panel/accounts.json`,给对应用户对象加一行 `"resetPassword": true`:
-
-```json
-{
- "id": "...", "username": "admin", "role": "admin",
- "passwordHash": "...", "disabled": false,
- "resetPassword": true
-}
-```
-
-```bash
-docker compose up -d # 3) 启动,面板初始化时会重置该账号
-```
-
-> ⚠️ 重置逻辑只在面板**进程启动**时执行。若你没先 `stop` 而面板仍在运行,直接 `docker compose up -d` 会因「容器无变化」而空操作(输出 `Running` 而非 `Started`),重置不会发生。此时执行 **`docker compose restart panel`**(或 `docker restart woc-panel`)强制重启即可生效。
-
-重启后该账号密码被重置为 `PANEL_ADMIN_PASSWORD`(即 `.env` 的 `WOC_PASSWORD`,默认 `wechat`),并自动**解禁**、清除该标记;用此密码登录后请立即在「修改密码」改掉。日志会打印 `[store] 已重置用户 '<用户名>' 的密码`。
-
----
-
-## 目录结构
-
-```
-WechatOnCloud/
-├── .github/workflows/
-│ └── release.yml # 打 tag / 发 Release 时构建多架构镜像并推送 GHCR
-├── docker/ # 微信实例镜像(ghcr.io//wechat-on-cloud)
-│ ├── Dockerfile # KasmVNC base + 中文字体 + 微信运行时依赖 + xdotool + 默认开 IME(不打包微信本体)
-│ ├── wechat-ctl.sh # 运行时下载/解压/更新微信(面板经 docker exec 触发,状态写 /config/.woc-state)
-│ ├── autostart # openbox 会话启动:常驻拉起微信(崩溃自重启)+ 最小化窗口自动复原看守
-│ └── woc-update-autostart # 启动钩子:每次启动用镜像内最新 autostart 覆盖数据卷旧副本
-├── panel/ # 自研面板(ghcr.io//woc-panel,唯一对外入口)
-│ ├── Dockerfile # 前端 Vite 打包 + 后端 Fastify 网关(多架构)
-│ ├── server/ # Fastify:cookie 鉴权 + 账号/实例/权限/生命周期 API + dockerode + 反代
-│ └── web/ # React + TS + PWA(微信 PC 式布局,牛奶布艺 + 微信绿主题)
-├── fnos/ # 飞牛 fnOS 应用打包(.fpk 工程 + 构建说明)
-├── scripts/
-│ └── build-local.sh # 本地构建面板+微信镜像(发布前自测 / 自托管自构建)
-├── doc/ # 文档与素材
-│ ├── 技术方案.md # 完整设计文档
-│ └── img/ # logo 与界面截图
-├── docker-compose.yml # 单服务:panel(挂 docker.sock,按需创建实例)
-├── .env.example # 可选配置(账号密码、镜像版本、PUID/PGID、端口、时区)
-└── README.md
-```
-
-数据:面板账号(含密码哈希)在 `./data-panel`,各微信实例在 docker 命名卷 `woc-data-`;`./data-panel` 已在 `.gitignore` 中。
+- **跨设备文本**:实例工具栏的「剪贴板」可把文本送入容器剪贴板,再在微信里 `Ctrl+V` 粘贴——不依赖浏览器异步剪贴板 API,**局域网 http 访问下也可用**。
+- **文件**:用工具栏「文件」拖拽上传,微信收到的文件另存到桌面即可在此下载。
---
@@ -398,6 +152,7 @@ WechatOnCloud/
- [x] 微信本体运行时下载到数据卷:面板一键「下载并安装 / 更新」,带进度条
- [x] 多实例管理 + 按账号的实例访问权限(RBAC)
- [x] 预构建多架构镜像发布到 GHCR + GitHub Actions 自动化
+- [x] 中文输入修复 + 文本剪贴板中转 + 实例日志导出
- [ ] 面板外层 TLS / 陌生设备验证码 / 审计日志
- [x] 多端并发控制(操作控制权心跳软锁 + 只读遮罩 + 申请接管)
- [ ] 掉登录时 web 端二维码重扫入口
@@ -425,4 +180,4 @@ WechatOnCloud/
如果这个项目帮到了你,欢迎点个 ⭐ Star 支持一下 ·
反馈问题 ·
参与贡献
-
\ No newline at end of file
+
diff --git a/doc/发布到GHCR.md b/doc/发布到GHCR.md
new file mode 100644
index 0000000..4d3505d
--- /dev/null
+++ b/doc/发布到GHCR.md
@@ -0,0 +1,58 @@
+# 发布到 GHCR
+
+> 返回 [← README](../README.md)
+
+把两个镜像(`woc-panel`、`wechat-on-cloud`)发布到 GitHub Container Registry,供他人 `docker compose up -d` 直接拉取。两种方式任选其一。
+
+---
+
+## 方式 A · GitHub Actions(推荐)
+
+仓库自带 GitHub Actions([.github/workflows/release.yml](../.github/workflows/release.yml)),在你**推送 `vX.Y.Z` 标签或发布 Release** 时,自动构建多架构(amd64+arm64)镜像并推到 GHCR:
+
+```bash
+git tag v1.0.0
+git push origin v1.0.0 # 触发 Actions,产出 ghcr.io//woc-panel:1.0.0 等标签
+# 或在 GitHub 上 Publish 一个 Release(会额外打 latest):
+gh release create v1.0.0 --title v1.0.0 --notes "..."
+```
+
+> 注意:单纯 push tag 只产出 `X.Y.Z / X.Y / X`,**不会更新 `latest`**;要更新 `latest` 请改用 **发布 Release** 或在 Actions 里手动 `workflow_dispatch`。
+
+---
+
+## 方式 B · 本机 buildx 手动构建并推送(不走 Actions)
+
+适合想立刻出包、或不依赖 CI 的场景。需要 Docker Buildx(Docker Desktop 自带;纯 Linux 跨架构需先装 QEMU:`docker run --privileged --rm tonistiigi/binfmt --install all`)。
+
+```bash
+# 1) 登录 GHCR(PAT 需 write:packages 权限)
+echo | docker login ghcr.io -u --password-stdin
+
+# 2) 首次创建并启用多架构构建器(已建过改用 docker buildx use woc)
+docker buildx create --name woc --use
+
+# 3) 构建并推送两个镜像(amd64 + arm64)。VER 与 git tag 保持一致(不带 v)
+VER=1.0.1
+docker buildx build --platform linux/amd64,linux/arm64 \
+ -t ghcr.io/gloridust/woc-panel:$VER -t ghcr.io/gloridust/woc-panel:latest \
+ --push ./panel
+docker buildx build --platform linux/amd64,linux/arm64 \
+ -t ghcr.io/gloridust/wechat-on-cloud:$VER -t ghcr.io/gloridust/wechat-on-cloud:latest \
+ --push ./docker
+```
+
+> 把 `gloridust` 换成你的 GHCR 命名空间(与 `docker-compose.yml` / `WOC_IMAGE_PREFIX` 一致)。
+> 只想本机自用、不推 GHCR,用 [`./scripts/build-local.sh`](../scripts/build-local.sh) 构建本机架构单架构镜像即可。
+
+---
+
+## 发布后:把包设为公开
+
+首次发布后还需把 GHCR 包设为公开,否则别人 `docker compose up -d` 会报 `denied`:
+
+1. 打开 GitHub → 你的头像 → **Packages** → 分别进入 `woc-panel`、`wechat-on-cloud`;
+2. **Package settings → Change visibility → Public**。
+
+> 若想保持私有,则使用者需先 `docker login ghcr.io`(用具备 `read:packages` 的 PAT)才能拉取。
+> 在镜像发布之前,本地用 [`./scripts/build-local.sh`](../scripts/build-local.sh) 自构建即可,无需等待发布。
diff --git a/doc/运行原理.md b/doc/运行原理.md
new file mode 100644
index 0000000..0808445
--- /dev/null
+++ b/doc/运行原理.md
@@ -0,0 +1,119 @@
+# 运行原理与 Docker 指南
+
+> 返回 [← README](../README.md) · 深入设计见 [技术方案.md](技术方案.md)
+
+本篇把「云微怎么跑起来」讲透:先一句话原理 + 架构图,再面向 Docker 新手的逐步拆解。
+
+---
+
+## 工作原理(一句话)
+
+每个微信实例 = 一个容器,里面跑 Xvfb 虚拟显示 + 官方原版微信,KasmVNC 把画面串到浏览器。同一实例被多个浏览器连 = 共享同一个微信会话。**不修改微信客户端**。
+
+前面一层自研 **面板(panel)** 是唯一对外入口:负责账号登录、子账号与**实例权限**管理,经 docker 引擎**按需创建/销毁**微信实例容器,并反向代理到对应实例——浏览器只和面板打交道,KasmVNC 的凭据由面板在服务端注入,不下发前端。
+
+```
+浏览器 ──▶ panel(:36080) ──┬─ / 面板 SPA(登录 / 实例网格 / 子账号 / 进入桌面)
+ cookie 鉴权 ├─ /api/* 账号、实例、权限接口
+ └─ /desktop/:id/* 反代 → 对应实例 KasmVNC(注入 Basic 鉴权)
+
+panel ──(docker.sock)──▶ docker 引擎 ──▶ 按需创建/销毁微信实例容器 woc-wx-
+ 每个实例 = 独立容器 + 独立数据卷 + 独立微信会话
+ 实例只在 docker 网络内暴露,不直连宿主
+```
+
+---
+
+## Docker 运行模式详解(新手向)
+
+如果你对 Docker 不熟,这一节把本项目「怎么跑起来的」讲透。读完你就能看懂上面的图。
+
+### 0. 先认识 5 个 Docker 概念
+
+| 概念 | 一句话理解 | 在本项目里是什么 |
+|------|-----------|------------------|
+| **镜像 Image** | 只读的「软件安装包」,里面装好了程序和依赖 | `woc-panel`(面板)、`wechat-on-cloud`(微信实例) |
+| **容器 Container** | 镜像「运行起来」的实例,相当于「正在跑的程序」。一个镜像能跑出多个容器 | `woc-panel` 容器、多个 `woc-wx-` 容器 |
+| **卷 Volume** | 容器之外的持久磁盘。容器删了,卷里的数据还在 | 每个微信实例一个卷 `woc-data-`,存登录态和消息 |
+| **网络 Network** | 容器之间互通的「虚拟局域网」,容器之间可用**容器名**当域名互访 | 面板和所有实例在同一网络里,面板用 `http://woc-wx-:3000` 找实例 |
+| **docker.sock** | Docker 引擎的「遥控器」(宿主上的一个特殊文件 `/var/run/docker.sock`)。谁拿到它,谁就能指挥 Docker 创建/删除容器 | 挂进面板容器,面板才能「动态造微信实例」 |
+
+> **Compose** 则是「用一个 `docker-compose.yml` 文件描述要跑哪些容器,`docker compose up -d` 一条命令拉起」。本项目的 compose 里**只有面板一个服务**。
+
+### 1. 本项目有两类容器(运行角色不同)
+
+这是本项目最容易迷惑的地方:**不是所有容器都写在 `docker-compose.yml` 里。**
+
+| | ① 面板容器 | ② 微信实例容器 |
+|---|-----------|---------------|
+| 容器名 | `woc-panel`(固定一个) | `woc-wx-<随机id>`(可有多个) |
+| 用哪个镜像 | `woc-panel` | `wechat-on-cloud` |
+| 谁来启动 | **你** 执行 `docker compose up -d` | **面板**:你在网页点「新建微信实例」时,面板通过 docker.sock 自动 `docker run` |
+| 写在 compose 里吗 | 是 | **否**(运行期动态创建,compose 里看不到) |
+| 对外暴露端口 | 是,宿主 `36080` → 容器 `8080` | 否,只在 docker 网络内,由面板反代 |
+| 数据存哪 | 宿主目录 `./data-panel` | 各自的命名卷 `woc-data-` |
+| 生命周期 | 常驻 | 你在面板「删除实例」时销毁(默认保留卷) |
+
+一句话:**你只手动管面板这一个容器;微信实例是面板帮你按需开关的。** 这就是为什么面板要挂 `docker.sock`——它需要「遥控」Docker 去开关微信实例容器。
+
+### 2. 镜像从哪来:两种「构建/获取」模式
+
+容器要跑,先得有镜像。本项目的两个镜像有两条获取途径,**任选其一**([README 快速开始](../README.md#快速开始)对应方式 A / B):
+
+| | 方式 A · 本地自构建 | 方式 B · 拉取官方镜像 |
+|---|--------------------|----------------------|
+| 怎么做 | `./scripts/build-local.sh`(用本仓库 Dockerfile 在你机器上造镜像) | `docker compose up -d`(自动从 GHCR 下载现成镜像) |
+| 适合谁 | 官方还没发布镜像时 / 想自己改代码 / 内网无法访问 GHCR | 普通用户,开箱即用 |
+| 前提 | 本机能拉到基础镜像(node、KasmVNC base) | GHCR 上已发布且包为公开(见[发布到 GHCR](发布到GHCR.md)) |
+| 产物 | 本地镜像,标签和 compose 里写的一模一样 | 同名镜像,来自云端 |
+
+> compose 的拉取策略是默认值(`missing`):**本地已有同名镜像就直接用,没有才去 GHCR 拉**。所以方式 A 构建完,`docker compose up -d` 会直接用你的本地镜像,不会再联网。想升级到 GHCR 最新版:`docker compose pull && docker compose up -d`。
+
+> 第三个「镜像」其实是**微信本体**:它**不打进任何镜像**,而是你在面板点「下载并安装」时,由实例容器实时从腾讯官方 CDN 下到自己的卷里(见[部署与运维 · 数据持久化](部署与运维.md#数据持久化))。
+
+### 3. 从零到能用,整体发生了什么
+
+```
+你: docker compose up -d
+ └─▶ Docker 读取 docker-compose.yml
+ └─▶ 拉起【面板容器 woc-panel】,挂上 ./data-panel 和 docker.sock,暴露 36080 端口
+
+你: 浏览器开 http://NAS:36080 → 登录 → 点「新建微信实例」
+ └─▶ 面板通过 docker.sock 指挥 Docker:
+ ├─ docker run 一个【微信实例容器 woc-wx-xxx】
+ ├─ 给它挂一个新卷 woc-data-xxx(存登录态/消息)
+ └─ 接到同一个 docker 网络(面板才能反代到它)
+
+你: 进入该实例 → 点「下载并安装」
+ └─▶ 面板 docker exec 进实例容器,触发脚本从腾讯 CDN 下载微信、解压到卷
+
+你: 点「进入电脑版微信」→ 手机扫码
+ └─▶ 浏览器 ⇄ 面板(反代+注入鉴权) ⇄ 实例容器的 KasmVNC ⇄ 微信窗口
+```
+
+### 4. 常用命令速查
+
+```bash
+docker compose up -d # 启动面板(首次会拉/用镜像)
+docker compose down # 停止并删除面板容器(不动数据卷和微信实例)
+docker compose pull # 把面板/微信镜像更新到 GHCR 最新
+docker ps # 看正在运行的容器(能看到 woc-panel 和各 woc-wx-*)
+docker logs -f woc-panel # 看面板日志
+docker logs -f woc-wx- # 看某个微信实例日志
+docker volume ls | grep woc # 看所有微信实例的数据卷
+```
+
+> ⚠️ 微信实例容器请**始终在面板网页里增删**,不要手动 `docker rm` 它们——否则面板的实例登记和真实容器会对不上。
+
+---
+
+## 架构自动适配
+
+镜像本身多架构(amd64/arm64);下载微信时容器内**运行时再自动检测 CPU 架构**(`dpkg --print-architecture`)取对应官方包:
+
+| 运行机器 | 架构 | 自动下载 |
+|----------|------|----------|
+| Intel/AMD NAS、x86 服务器 | amd64 | `WeChatLinux_x86_64.deb` |
+| ARM NAS、Apple Silicon Mac | arm64 | `WeChatLinux_arm64.deb` |
+
+到飞牛上(无论 x64 还是 arm)`docker compose up -d` 同一条命令,无需改任何架构相关配置。
diff --git a/doc/部署与运维.md b/doc/部署与运维.md
new file mode 100644
index 0000000..ae3f3e4
--- /dev/null
+++ b/doc/部署与运维.md
@@ -0,0 +1,101 @@
+# 部署与运维
+
+> 返回 [← README](../README.md)
+
+数据持久化、常见问题排查、忘记超管密码的离线找回,以及仓库目录结构。
+
+---
+
+## 数据持久化
+
+- **面板数据**(用户、实例元信息、密码哈希):容器内 `/data`,映射到宿主 `./data-panel`。
+- **每个微信实例**:独立的 docker 命名卷 `woc-data-`,挂到该实例容器的 `/config`(微信本体在 `/config/wechat`,登录态与消息缓存在 `/config` 其余位置)。
+
+要点:
+- 删除实例**默认保留**其数据卷,下次同名重建可复用;只有显式勾选「彻底清除」才会删卷。
+- 备份某实例 = 备份对应的 `woc-data-` 卷(`docker volume` 系列命令)。
+- 卷需支持执行权限(微信本体直接从卷里运行);放在 `noexec` 卷上微信将无法启动。
+- 备份面板 = 备份 `./data-panel`。
+
+> **从旧版(单微信容器 + `./data` 绑定挂载)迁移**:旧形态把微信数据放在宿主 `./data`。新版用 docker 命名卷,结构不同,无自动迁移。如需保留旧会话,最简单是新建一个实例、重新扫码登录;或手动把旧 `./data` 内容拷进新实例的 `woc-data-` 卷。
+
+---
+
+## 常见问题
+
+| 现象 | 排查 |
+|------|------|
+| 新建实例失败 | 多为面板拉不到微信镜像或连不上 docker.sock。确认 `docker.sock` 已挂载、宿主能访问 GHCR;看面板日志 `docker logs woc-panel` |
+| 界面/消息显示成方块 | 中文字体没装好,确认实例镜像含 `fonts-noto-cjk` |
+| 微信起不来 / 黑屏 | 看实例日志 `docker logs woc-wx-`;确认 `seccomp=unconfined` 与 `shm_size` 生效。微信 deb 漏声明的运行时依赖已在 Dockerfile 内置 |
+| 排查缺哪个库 | `docker exec woc-wx- ldd /config/wechat/opt/wechat/wechat`,看 `not found` 项补进 Dockerfile 依赖层 |
+| 多人同时操作很乱 | 已内置「操作控制权」软锁:当前操作者每数秒心跳续约,其余端自动转为**只读遮罩**(仍可看画面),空闲超时(约 10s)自动释放,他人可点「申请控制」接管。仍建议同一时刻一人操作 |
+| 过段时间掉登录 | 微信桌面会话会定期失效,需手机重新扫码(见技术方案 6.2) |
+| 下载 / 更新微信失败 | 腾讯 CDN 偶发波动,重新点「下载并安装 / 更新」即可;脚本已内置主/备 CDN 自动回退 |
+| 架构不支持报错 | 微信仅提供 x86_64 / arm64;其他架构下载时会在面板状态里报错 |
+| 忘记超管密码 | 见下方「重置超管密码」离线找回 |
+
+查看面板日志:`docker logs -f woc-panel`;查看某实例日志:`docker logs -f woc-wx-`(实例 ID 可在面板看到,或 `docker ps | grep woc-wx`)。也可在面板的「管理」页或实例页点「查看日志」直接导出实例容器日志。
+
+---
+
+## 重置超管密码(离线找回)
+
+管理员密码无法被他人重置,忘记时按以下步骤离线找回:
+
+```bash
+docker compose stop panel # 1) 先停面板,避免覆盖你的手动修改
+```
+
+2) 编辑 `./data-panel/accounts.json`,给对应用户对象加一行 `"resetPassword": true`:
+
+```json
+{
+ "id": "...", "username": "admin", "role": "admin",
+ "passwordHash": "...", "disabled": false,
+ "resetPassword": true
+}
+```
+
+```bash
+docker compose up -d # 3) 启动,面板初始化时会重置该账号
+```
+
+> ⚠️ 重置逻辑只在面板**进程启动**时执行。若你没先 `stop` 而面板仍在运行,直接 `docker compose up -d` 会因「容器无变化」而空操作(输出 `Running` 而非 `Started`),重置不会发生。此时执行 **`docker compose restart panel`**(或 `docker restart woc-panel`)强制重启即可生效。
+
+重启后该账号密码被重置为 `PANEL_ADMIN_PASSWORD`(即 `.env` 的 `WOC_PASSWORD`,默认 `wechat`),并自动**解禁**、清除该标记;用此密码登录后请立即在「修改密码」改掉。日志会打印 `[store] 已重置用户 '<用户名>' 的密码`。
+
+---
+
+## 目录结构
+
+```
+WechatOnCloud/
+├── .github/workflows/
+│ └── release.yml # 打 tag / 发 Release 时构建多架构镜像并推送 GHCR
+├── docker/ # 微信实例镜像(ghcr.io//wechat-on-cloud)
+│ ├── Dockerfile # KasmVNC base + 中文字体 + 微信运行时依赖 + xdotool + IME 补丁(不打包微信本体)
+│ ├── woc-www-patch.sh # 构建期补丁:开启 KasmVNC IME 模式 + 修复 noVNC 中文输入(配合 woc-ime.pl)
+│ ├── woc-ime.pl # noVNC 键盘 IME 逻辑的 perl 补丁(合成提交时整串发送,规避丢字)
+│ ├── wechat-ctl.sh # 运行时下载/解压/更新微信(面板经 docker exec 触发,状态写 /config/.woc-state)
+│ ├── autostart # openbox 会话启动:常驻拉起微信(崩溃自重启)+ 最小化窗口自动复原看守
+│ └── woc-update-autostart # 启动钩子:每次启动用镜像内最新 autostart 覆盖数据卷旧副本
+├── panel/ # 自研面板(ghcr.io//woc-panel,唯一对外入口)
+│ ├── Dockerfile # 前端 Vite 打包 + 后端 Fastify 网关(多架构)
+│ ├── server/ # Fastify:cookie 鉴权 + 账号/实例/权限/生命周期 API + dockerode + 反代
+│ └── web/ # React + TS + PWA(微信 PC 式布局,牛奶布艺 + 微信绿主题)
+├── fnos/ # 飞牛 fnOS 应用打包(.fpk 工程 + 构建说明)
+├── scripts/
+│ └── build-local.sh # 本地构建面板+微信镜像(发布前自测 / 自托管自构建)
+├── doc/ # 文档与素材
+│ ├── 技术方案.md # 完整设计文档
+│ ├── 运行原理.md # 工作原理 + Docker 运行模式详解(新手向)
+│ ├── 发布到GHCR.md # 镜像发布到 GHCR 的两种方式
+│ ├── 部署与运维.md # 本文档:数据持久化 / 常见问题 / 密码找回 / 目录结构
+│ └── img/ # logo 与界面截图
+├── docker-compose.yml # 单服务:panel(挂 docker.sock,按需创建实例)
+├── .env.example # 可选配置(账号密码、镜像版本、PUID/PGID、端口、时区)
+└── README.md
+```
+
+数据:面板账号(含密码哈希)在 `./data-panel`,各微信实例在 docker 命名卷 `woc-data-`;`./data-panel` 已在 `.gitignore` 中。