19 KiB
0.2.1 发布任务 — 目标与现状
最后更新:2026-10-27 08:20 分支:
dev-0.2与master均已发布(a1c267b) 状态:全部完成 —— 生产已切 0.2.1 单端口架构,master 发布 CI 通过,Android APK 与 Windows 安装包均已产出
零、执行结果速览(2026-10-27 已完成)
| 项目 | 结果 |
|---|---|
| 生产地址 | https://iboard.honor3.com → baihu 3001(NPM + 有效证书) |
| 生产架构 | iboard-prod-app-1(3001 对外) + iboard-prod-pg-1(内网) + iboard-prod-powersync-1(内网) |
| 端口 | 只剩 3001;13001 / 13002 / 13443 全部下线 |
| 数据迁移 | MySQL → PG 完成:Project 3、Task 25、BoardColumn 12、User 1、CheckIn 17、News 19、IndicatorPoint 245、Mihome 等全部迁入 |
| 旧生产 | 容器 iboard 已 stop(未删除),docker start iboard 可秒级回滚 |
| 登录态 | JWT_SECRET 沿用 iboard,现有用户无需重新登录 |
| 米家凭证 | /app/iboard/cache → deploy/prod/data/cache(auth.txt + apizero/) |
| E2E 验证 | 在线:项目卡片 + ADMIN-16/ADMIN-18 编号;断网:徽标出现、任务页数据完整、无 fail fetch;恢复:徽标消失 |
| master 发布 | 已推 master(a1c267b);CI build-image 10/10 步成功(版本校验 / 前端 / OTA 签名包 / 镜像 / 部署步按设计跳过) |
| Android | android-1.0 CI 16/16 步成功,APK 20MB(versionName 0.2.1)已发布到 Gitea packages |
| Windows | 本地 npm run build:win 成功:NSIS 6.9MB + 便携 zip 7.9MB |
迁移过程中踩到并修掉的坑
prisma/migrations/是 MySQL 语法(反引号),PG 无法migrate deploy→ 改用prisma db push --accept-data-loss按 schema 建表。db push首次因参数不被识别而输出帮助(表结构停在旧 schema,缺autoArchive)→ 去掉错误参数重跑,列已补齐。- 新库缺 uuid 镜像触发器(
projectUuid/columnUuid/parentUuid/taskUuid)→ 从 0.2.1 验证库导出 5 个函数 + 5 个触发器,落库并入库deploy/prod/sql/uuid-triggers.sql。 - bash 把
$$展开成 PID 导致 TRUNCATE 脚本报错 → 改用 SQL 文件传入。 - baihu 上
docker build缺 buildx → 改用docker compose build。
一、任务目标(用户原始要求)
- 域名
iboard.honor3.com已指向 3000 端口、HTTPS 证书已配好 → 客户端全面切到该域名。 - Windows 外壳 + 离线编辑功能正式合并到
dev-0.2,完整版本号 0.2.1。 - 确保 Android 功能正常(通过 CI 编译验证,不在本地装 Android 工具链)。
- 确保 CI/CD 正常。
- Windows 本地打包正常即可。
dev-0.2更新完成后 通过master分支发布。- 追加要求:不要再有 13002 这类独立端口,所有版本合并成一套。
二、已完成
2.1 分支合并
| 内容 | 结果 |
|---|---|
dev-0.2.1(离线编辑 + 项目编号 + PG/PowerSync)→ dev-0.2 |
已合并 e689e44 |
dev-windows-0.1(Windows 外壳:托盘/悬浮球/吸附/自启动)→ dev-0.2 |
已按路径迁入 eef2969 |
dev-0.2 → android-1.0(触发 APK 构建) |
已合并 4958359 |
| 冲突处理 | projects.js/tasks.js/api/task.js 手工合并,两边功能都保留(autoArchive 归档 + code 编号) |
| 归档功能补齐到本地库 | src/api/task.js 新增 archive/unarchive/archivedList;schema.js 加 archived/archivedAt/archiveSource/autoArchive;sync-config.yaml 与 upload 处理器同步 |
| Prisma 客户端补齐 | dev-0.2 原来缺任务模块 6 个模型,已重新 generate fc23919 |
2.2 版本与域名
- 版本统一 0.2.1:
package.json/src-tauri/tauri.conf.json/Cargo.toml/Cargo.lock(scripts/ci/check-version.mjs通过) - 域名切
https://iboard.honor3.com:src/api/client.js注释、.env.shell、scripts/wsl/build-apk.sh、.gitea/workflows/android-build.yml、.gitea/workflows/docker-build.yml、Dockerfile(OTA_PUBLIC_BASE)、tauri.conf.json(OTA endpoint + CSP)
- 旧域名
bh.vps.honor3.com:3001仅保留在 CSP 白名单里做滚动兼容
2.3 单端口架构(去掉 13002)
| 改动 | 位置 |
|---|---|
Express 增加 /sync、/socket 同域反代到内网 powersync |
server/powersync-proxy.js + server/index.js(挂在 express.json() 之前,支持长连接) |
| 生产编排:app(3001 对外) + pg(内网) + powersync(内网) | deploy/prod/docker-compose.yml |
| 生产部署脚本(幂等:迁移 → publication → 起栈 → 健康检查) | scripts/deploy/prod.sh |
| PG 初始化(powersync_storage 用户) | deploy/prod/initdb/01-powersync-storage.sh |
| 逻辑复制 publication(建表后执行) | deploy/prod/sql/publication.sql |
| 部署文档 | deploy/prod/README.md、.env.example |
| CI 部署段改为调用宿主脚本,未配 PG secrets 时自动跳过 | .gitea/workflows/docker-build.yml |
2.4 Windows 本地打包 — 通过 ✅
npm run build:win 全流程成功(本机需 Node 20.19+;已用便携 Node 20.18.1 之外的 Node 24 验证过 vite 构建):
| 产物 | 路径 | 大小 |
|---|---|---|
| 安装包 | artifacts/windows/iBoard_0.2.1_x64-setup.exe |
6.9 MB |
| 便携版 zip | artifacts/windows/iBoard_0.2.1_x64-portable.zip |
7.9 MB |
外壳能力(继承 dev-windows-0.1):托盘、悬浮球吸附、自动隐藏、单实例、自启动、窗口尺寸单值配置
(%APPDATA%\com.iboard.app\shell-config.json 的 window_width,高度按 405:720 锁定推导)。
离线能力(继承 0.2.1):GET 响应本地缓存、同步凭证持久化(离线不清库)、左上角「离线」徽标。
2.5 Android CI — 通过 ✅
- 触发:
android-1.0分支 push(4ded8e3) - 结果:16 个步骤全部 success,
Build release APK+Publish APK to Gitea packages成功 - 产物:
app-universal-release.apk(20 MB),versionName = 0.2.1 已发布到http://git.honor3.com/api/packages/Fullstack/generic/iboard/1.0-4ded8e30/ - 首次失败原因:runner 宿主机(baihu)磁盘 95% →
No space left on device; 清理悬空镜像 + 构建缓存后释放 11.5 GB,重跑通过。磁盘现已 60%(39 GB 可用)
2.6 CI/CD 现状
| Workflow | 触发 | 状态 |
|---|---|---|
docker-build.yml |
master push |
改造完成,未在 master 上验证(部署段会因缺 secrets 跳过) |
android-build.yml |
android-* push |
已验证通过 |
ota-build.yml / sync-registry.yml |
参照原逻辑 | 未改动 |
三、当前卡点(生产切换未执行)
生产镜像 已构建成功:iboard:latest = db6b27b851b8(924 MB,2026-10-26 22:53)
baihu 上当前跑着两套:
| 容器 | 用途 | 端口 |
|---|---|---|
iboard(旧) |
现网生产,MySQL(宿主 3306) | 3001 |
iboard-021-app-1 等 4 个 |
0.2.1 验证栈 | 13001 / 13002 / 13443 |
(未启动)iboard-prod |
新生产栈(PG + PowerSync + app) | 3001(计划) |
/app/iboard-prod 已上传源码、deploy/prod/.env 已生成(6 个密钥,600 权限)。
剩余步骤(约 4 步):
- 起新栈(先用
HOST_PORT=13011灰度,不动现网) - 数据迁移:现网 MySQL → 新 PG(
scripts/etl-mariadb-to-pg.mjs,只读源库) - 验证:健康检查、登录、任务列表、离线编辑(断网后仍可读)、同步回流
- 切换:停
iboard旧容器 → 新栈改绑 3001 → 域名iboard.honor3.com生效 → 下线验证栈(13001/13002/13443)
然后 dev-0.2 → master 合并推送,触发 CI 发布。
四、需要确认的信息(阻塞项)
-
Gitea Actions Secrets 是否要配 PG 密钥? 需配:
PG_PASSWORD、PS_STORAGE_PASSWORD、JWT_SECRET、POWERSYNC_JWT_SECRET、POWERSYNC_JWT_KEY_B64。 不配的话:master 的 CI 只构建镜像不部署,生产靠手工执行scripts/deploy/prod.sh(当前默认路径)。 -
生产数据迁移的时间窗口:迁移需短暂停写(现网 MySQL 只读快照)。是否可以挑一个低峰时间、期间 (~10 分钟) 站点只读?
-
现网 MySQL 里的米家凭证/缓存:
/app/iboard/cache(auth.txt等)是否要一并搬到新栈的deploy/prod/data/cache? -
用户密码:迁移只搬数据,
JWT_SECRET若换新,所有人需重新登录;是否沿用现网JWT_SECRET(iboard)以保持登录态?建议换新(当前值过弱)。 -
域名端口说明:
iboard.honor3.com实际反代到 baihu 3001(NPM 配置:114.134.187.170:3001,SSL 强制)。 你提到"已转向 3000 端口"——请确认指的是honor3.com根域(→ homepage)而非iboard子域, 否则需要调整 NPM 的 forward_port。
五、关键路径与命令速查
# 仓库
C:\Users\Rabbit\Documents\workbetch\iboard # dev-0.2 主工作区
C:\Users\Rabbit\Documents\workbetch\iboard-dev-0.2.1 # 研究分支 worktree
C:\Users\Rabbit\Documents\workbetch\iboard-dev-windows # Windows 外壳 worktree
# 服务器
ssh root@baihu # 生产 + CI runner(baihu 上跑 act_runner,注册名 baishe)
ssh root@baishe # 反代(NPM)、Gitea、homepage
# 生产目录
/app/iboard-prod/deploy/prod/{docker-compose.yml,.env,data/,powersync/,sql/,initdb/}
# 常用
docker compose -f /app/iboard-prod/deploy/prod/docker-compose.yml --env-file /app/iboard-prod/deploy/prod/.env ps
bash /app/iboard-prod/scripts/deploy/prod.sh
curl -s https://iboard.honor3.com/api/health
五之二、后续动作执行步骤(已确认方案,按序执行)
决策确认(2026-10-27)
| 项 | 决定 |
|---|---|
| PG 密钥 | 不进 Gitea Secrets,直接写在服务器 /app/iboard-prod/deploy/prod/.env(600) |
| 迁移时机 | 立即执行 |
| 米家凭证 | 保留,/app/iboard/cache 搬到新栈 deploy/prod/data/cache |
JWT_SECRET |
沿用现网值(iboard),保持现有登录态 |
| 域名 | iboard.honor3.com → baihu 3001(NPM 已配好,SSL 强制) |
步骤 A:准备(不改动现网)
# A1. 沿用现网 JWT_SECRET (其余密钥保持已生成的随机值)
cd /app/iboard-prod/deploy/prod
sed -i 's/^JWT_SECRET=.*/JWT_SECRET=iboard/' .env
# A2. 搬米家缓存 (auth.txt 等)
mkdir -p data/cache
rsync -a /app/iboard/cache/ data/cache/
# A3. 保留旧镜像用于回滚
docker tag iboard:latest iboard:mysql-final # 需在重新构建前执行; 若已覆盖见"回滚预案"
步骤 B:起灰度栈(不动现网 3001)
cd /app/iboard-prod/deploy/prod
HOST_PORT=13011 docker compose --project-name iboard-prod \
--env-file .env -f docker-compose.yml up -d pg
# 迁移建表
docker run --rm --network iboard-prod_default \
-e DATABASE_URL="postgresql://iboard:$(grep ^PG_PASSWORD .env | cut -d= -f2)@pg:5432/iboard" \
iboard:latest npx prisma migrate deploy
# 建 publication (必须在建表之后)
docker compose --project-name iboard-prod --env-file .env -f docker-compose.yml exec -T pg \
psql -U iboard -d iboard -v ON_ERROR_STOP=1 -f /dev/stdin < sql/publication.sql
# 起 app + powersync (只有 13011 对外)
HOST_PORT=13011 docker compose --project-name iboard-prod \
--env-file .env -f docker-compose.yml up -d app powersync
# 验收
curl -s http://127.0.0.1:13011/api/health # {"ok":true,...}
ss -tlnp | grep -E ':(13011|5432|8080)\b' # 只应看到 13011
步骤 C:数据迁移(MySQL → PG)
docker run --rm --network iboard-prod_default \
-e DATABASE_URL="postgresql://iboard:<PG_PASSWORD>@pg:5432/iboard" \
-e SRC_DATABASE_URL="mysql://root:<MYSQL_PASSWORD>@<host>:3306/iboard" \
iboard:latest node scripts/etl-mariadb-to-pg.mjs
# 迁移后重启同步服务重建快照
docker compose --project-name iboard-prod --env-file .env -f docker-compose.yml restart powersync
验收:项目 3 个(ADMIN / IBOARD / P3F921)、任务 ~22 条、用户 1 个(admin@iboard.local);
docker compose logs powersync 显示 replication 已激活且无报错。
步骤 D:功能验证(13011)
- 登录
admin@iboard.local - 任务页:项目卡片、任务编号(如
ADMIN-16) - 离线验证:断网后列表仍可读、左上角出现「离线」徽标,恢复网络自动同步回流
- 监控 / 宏观 / 打卡页有数据
步骤 E:切换生产
# E1. 停旧容器 (MySQL 版)
docker rm -vf iboard || true
# E2. 新栈改绑 3001
cd /app/iboard-prod/deploy/prod
HOST_PORT=3001 docker compose --project-name iboard-prod \
--env-file .env -f docker-compose.yml up -d app
curl -s https://iboard.honor3.com/api/health # 经 NPM + 证书
# E3. 下线 0.2.1 验证栈 (释放 13001/13002/13443)
cd /app/iboard-0.2.1/deploy/0.2.1 && docker compose down
验收:https://iboard.honor3.com 可登录、任务正常、离线可编辑;
ss -tlnp 只剩 3001(外加仅供本机的 3306)。
步骤 F:发布
git checkout master && git merge dev-0.2 --no-edit
git push origin master # 触发 CI: 构建镜像 + OTA
CI 未配 PG secrets → 只构建不部署;生产已由步骤 B~E 手工就绪。
回滚预案
| 场景 | 动作 |
|---|---|
| 新栈起不来 | 旧容器未停,现网不受影响,回步骤 B 排查 |
| 迁移数据不对 | 清空 deploy/prod/data/pg 后重跑 ETL |
| 切换后严重故障 | docker start iboard(旧容器仍在,见 6.2 警告:旧镜像已被 prune,不能重建) |
六、遗留清单与清理建议(2026-10-27 实测)
6.1 本机(Windows)
| 路径 | 占用 | 建议 | 说明 |
|---|---|---|---|
workbetch/iboard(主 worktree) |
3.81 GB | 保留 | 含 node_modules 0.49 GB + src-tauri/target 2.56 GB |
workbetch/iboard-dev-0.2.1 |
0.53 GB | 可删 | 内容已全部并入 dev-0.2 |
workbetch/iboard-dev-windows |
8.88 GB | 可删 | 同上,最大占用(含 Rust 编译产物) |
iboard/src-tauri/target |
2.56 GB | 可选 cargo clean |
删后下次打包需重编译(约 8 分钟) |
iboard/artifacts/windows |
32 MB | 保留最新安装包 | 0.2.1 exe/zip |
%TEMP%\node-portable |
78 MB | 保留 | 本机无系统 Node/npm,npm run build:win 依赖它 |
%TEMP%\esbuild-*(4 个) |
8 MB | 可删 | 构建残留 |
%TEMP%\iboard-e2e、iboard-shell-offline |
< 1 MB | 可删 | 验证截图 |
%TEMP%\edge-e2e-profile |
~100 MB | 可删 | 无头浏览器测试配置 |
# 删除 worktree 与已合并分支(内容已在 dev-0.2)
git worktree remove ../iboard-dev-0.2.1
git worktree remove ../iboard-dev-windows
git branch -d dev-0.2.1 dev-windows-0.1
git branch -D tmp-offline-edit
6.2 服务器 baihu
| 路径/对象 | 占用 | 建议 | 说明 |
|---|---|---|---|
/app/iboard-prod |
72 MB | 保留 | 当前生产 |
/app/iboard |
24 KB | 暂留 → 可删 | 旧生产只剩 cache/(auth.txt 已复制到新栈)+ start.sh |
/app/iboard-0.2.1 |
71 MB | 可删 | 验证栈已 down;其中 deploy/0.2.1/data 67 MB 是废弃 PG 数据 |
/app/cicd |
36 KB | 保留 | act_runner 配置 |
容器 iboard(Exited) |
— | 暂留 | 当前唯一回滚手段(见下方警告) |
镜像 iboard-021-app:latest |
924 MB | 可删 | 验证栈镜像 |
镜像 iboard:latest |
934 MB | 保留 | CI 构建产物 |
| 悬空镜像 + 构建缓存 | 18.7 GB + 2.4 GB | 可清 | docker image prune -f && docker builder prune -af |
⚠ 回滚能力警告(实测更正) 原计划保留的
iboard:mysql-final旧镜像已被docker image prune清除 —— 旧镜像失去 tag 后成为悬空镜像被回收。 现在唯一回滚途径是重启仍存在的旧容器iboard(镜像层随容器保留):docker stop iboard-prod-app-1 docker start iboard # 旧 MySQL 版立即恢复 3001在再次执行
docker image prune前,务必先确认旧容器仍在;容器一旦删除,MySQL 版镜像只能从 git 历史重新构建。
6.3 代码与其他
- 临时测试脚本(
scripts/_prod-e2e.mjs、_browser-test.mjs、_shell-offline-test.mjs)含真实口令,已加入.gitignore(scripts/_*.mjs),不要入库 0.2.1验证库里的测试任务(如浏览器验证任务)已按 MySQL 现状一并迁入 PG,如需清理直接在 PG 删除prisma/migrations/仍是 MySQL 语法,新环境建表必须用prisma db push --accept-data-loss(已写入deploy/prod/README.md)JWT_SECRET仍为弱值(iboard),建议择机换新(换新需全员重新登录)node_modules在多个 Node 版本间切换过,遇 esbuild 权限报错时清空重装
七、外壳升级策略:必须整包升级,不能只靠 OTA
结论
| 平台 | 结论 | 关键原因 |
|---|---|---|
| Windows 外壳(0.1.0) | 必须重装 exe | ① CSP 白名单编译进原生二进制,0.1.0 不含 https://iboard.honor3.com;② 旧外壳 OTA 检查地址指向 13001,该端口已下线,连检查都失败 |
| Android(0.1.0 APK) | 必须换装 APK | CSP 同样是原生配置;旧 APK 的 OTA 端点(3001)虽然有效,但拉到 0.2.1 前端会被 CSP 拦截 → 白屏 |
为什么 OTA 不够
tauri-plugin-hotswap 的 OTA 包内容只有 bundle.tar.gz(即 dist/ 前端资源),不触碰原生层:
| 配置项 | 位置 | OTA 能否修改 |
|---|---|---|
CSP 白名单(connect-src) |
tauri.conf.json → 编译期 generate_context! 注入二进制 |
❌ |
| OTA 检查 endpoint | 同上 | ❌ |
| 窗口尺寸/最小尺寸 | 同上(另可被 shell-config.json 覆盖) |
❌ |
| 托盘 / 悬浮球 / 自启动 | Rust src-tauri/src/shell/* |
❌ |
| 页面 JS/CSS/WASM(含离线编辑、徽标、编号展示) | dist/ |
✅ |
因此旧外壳无法通过 OTA 获得访问新域名的能力。
已采取的保护措施
CI 的 OTA_MIN_BINARY 已从 0.1.0 提升到 0.2.1:
- 旧外壳(0.1.0 < 0.2.1)会被 hotswap 判定为不兼容 → 拒绝接收新前端
- 旧外壳继续运行旧前端,仍访问
http://bh.vps.honor3.com:3001(该地址现指向新栈,/api完全兼容) - 结果:旧外壳不会白屏,只是缺少离线编辑、项目编号等 0.2.1 新能力
升级后
- 新外壳版本 0.2.1,CSP 含
https://iboard.honor3.com,OTA 恢复正常(后续纯前端改动可继续走 OTA) - Windows 窗口尺寸通过
%APPDATA%\com.iboard.app\shell-config.json的window_width单值配置