- 配置中心原 MINISIGN_PRIVATE_KEY 缺首行 untrusted comment (只含 base64), minisign 报 'Error while loading the secret key file' - 已删除坏条目 (cfg-mtwmbmkd-bbiihv), 保留正确完整值 (cfg-mtwr5c2m-99r5fh) - docker-build.yml / ota-build.yml 不再用 GITHUB_ENV 多行 heredoc, 改为把私钥写入 ota-package/hotswap.key, build-ota.mjs --key 显式指定 - 本地全链路验证: 配置中心拉取 → 写文件 → 签名 → 用 tauri.conf.json 公钥验签通过 |
||
|---|---|---|
| .gitea/workflows | ||
| ota-package | ||
| photograph | ||
| prototype | ||
| scripts | ||
| server | ||
| src | ||
| src-tauri | ||
| .dockerignore | ||
| .gitignore | ||
| .mise.toml | ||
| DESIGN.md | ||
| Dockerfile | ||
| index.html | ||
| MCP.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
Wardrobe - AI 衣物管理系统
基于 Tauri 2.x 封装的跨平台衣物管理应用,目标平台包括 Android / Windows / macOS。
功能特性
- 📸 数字衣橱 - 拍照录入、AI 识别、分类管理
- 💡 AI 搭配推荐 - 按季节/场合智能推荐
- 📅 穿搭日历 - 记录每日 OOTD
- 📊 风格统计 - 衣橱利用率、分类分析
- 🧺 清洁管理 - 清洁周期提醒
- 🔐 邮箱/手机注册 - 验证码认证 (暂定
1111) - 🗄️ MCP 服务 - AI 平台数据管理接口
- 🦆 DuckDB - 本地嵌入式数据库
技术栈
| 层 | 技术 |
|---|---|
| 前端 | React 18 + TypeScript + Vite + Zustand |
| 桌面壳 | Tauri 2 (WebView) |
| 后端 | Rust + DuckDB (bundled) |
| 移动端 | Tauri Android |
| 状态管理 | Zustand |
| 路由 | React Router v6 |
目录结构
├── src/ # React 前端
│ ├── api/ # Tauri IPC 封装
│ ├── components/ # 通用组件 (Layout)
│ ├── pages/ # 页面
│ │ ├── Landing.tsx # 项目首页 (响应式, 含 APK 下载)
│ │ ├── Login.tsx # 登录/注册 (邮箱/手机 + 验证码)
│ │ ├── Home.tsx # 首页/今日推荐
│ │ ├── Closet.tsx # 衣橱列表
│ │ ├── AddClothing.tsx # 添加衣物
│ │ ├── ClothingDetail.tsx
│ │ ├── Outfits.tsx # 搭配推荐
│ │ ├── Calendar.tsx # 穿搭日历
│ │ └── Profile.tsx # 我的 (含 OTA 升级区)
│ ├── api/
│ │ ├── index.ts # Tauri IPC 封装
│ │ └── ota.ts # OTA 封装 (Tauri + Web 双路径)
│ ├── version.ts # 版本/构建号 (vite define 注入)
│ ├── stores/ # Zustand 状态
│ ├── styles/ # 全局样式
│ └── types/ # TS 类型
├── server/ # OTA 服务端 (Express)
│ ├── index.js # 静态 dist + SPA fallback + /api/health
│ └── routes/ota.js # OTA check/bundle/manifest-web 接口
├── scripts/
│ ├── build-ota.mjs # OTA 打包 + minisign 签名 + latest.json
│ ├── ota-genkey.mjs # 生成 minisign 密钥对
│ ├── upload-ota-r2.sh # OTA 包上传 R2
│ └── ... # Android 构建/签名/上传脚本
├── src-tauri/ # Rust 后端
│ ├── src/
│ │ ├── main.rs # 桌面入口 (薄壳)
│ │ ├── lib.rs # 应用逻辑 + mobile_entry_point + hotswap 插件
│ │ ├── db.rs # DuckDB 数据层
│ │ ├── models.rs # 数据模型
│ │ ├── auth.rs # 认证 (验证码 1111)
│ │ └── mcp.rs # MCP 服务
│ ├── Cargo.toml
│ ├── tauri.conf.json # 含 plugins.hotswap (OTA 端点 + 公钥)
│ └── capabilities/default.json
├── prototype/ # HTML 交互原型
└── photograph/ # 竞品截图
快速开始
前置要求
- Node.js ≥ 18
- Rust ≥ 1.77
- Android Studio (Android 构建)
开发
npm install
npm run tauri dev # 桌面端开发
Android 构建
npm run tauri android init # 首次生成 Android 工程
npm run tauri android dev # Android 设备调试
npm run tauri android build # 生成 APK
关键设计
数据库 (DuckDB)
- 数据文件:
%APP_DATA%/wardrobe/wardrobe.duckdb, Android 下存于应用私有目录 - 表:
users,clothing_items,outfits,daily_records - 读写均通过
Mutex<Connection>串行化,保证线程安全
优雅停机
ctrlc信号捕获 → 设置 shutdown flag- 窗口关闭事件 → 记录日志、释放连接
- DuckDB 连接在进程结束时由 OS 正常关刷
验证码 (临时)
- 统一固定为
1111 - 有效期 5 分钟,成功后自失效
- 注册: 邮箱 或 手机号 (二选一) +
1111
MCP 服务
详见 MCP.md。通过 mcp_list_tools / mcp_call_tool IPC 命令暴露 10 个数据管理工具。
Android CI/CD
参考 D:\workbench\iboard 的 Gitea Actions 部署模式。
触发方式
推送 android-* 分支 (如 android/release) 自动触发构建。
流程 (.gitea/workflows/android-build.yml)
push android-* → checkout → mise( node/rust ) → JDK 17
→ 定位可复用 Android SDK (优先 runner 预装 /usr/local/lib/android/sdk, 其次 CI 缓存, 最后下载)
→ 缓存复用 NDK 27.2 / Rust 工具链 / Tauri target
→ tauri android init --ci
→ 从配置中心拉取全部密钥 (签名 + R2)
→ scripts/ci/android-signing.sh 注入签名
→ tauri android build --apk → 断言签名生效
→ scripts/upload-r2.sh 用 wrangler 上传 APK 到 R2 公开桶 (store/data/android/wardrobe.apk)
→ 输出公共下载地址 (https://pub-...r2.dev/data/android/wardrobe.apk)
CI secrets (仅配置中心连接)
| Secret | 说明 |
|---|---|
CONFIG_CENTER_URL |
配置中心地址 (未设时回退 http://bh.vps.honor3.com:8008) |
CONFIG_CENTER_TOKEN |
配置中心访问 token |
所有业务密钥 (签名 keystore / R2 token) 一律来自配置中心,不进 Gitea secrets。
配置中心存储约定
| 项目 | 分组 | key | 用途 |
|---|---|---|---|
service-wardrobe |
signing |
ANDROID_KEYSTORE_B64 |
keystore base64 (JKS RSA 2048) |
service-wardrobe |
signing |
ANDROID_KEY_ALIAS |
wardrobe |
service-wardrobe |
signing |
ANDROID_KEY_PASSWORD |
store/key 密码 |
service-wardrobe |
ota |
MINISIGN_PRIVATE_KEY |
OTA 包 minisign 私钥 (整段 .key, 多行) |
service-wardrobe |
ota |
CF_OTA_ENDPOINT |
OTA 检查端点 (部署参考) |
CommonExternalService |
cloudflare |
r2/CF_API_TOKEN |
Cloudflare API token (wrangler 上传) |
CommonExternalService |
cloudflare |
r2/CF_R2_PUBLIC_URL |
R2 公共访问域名 |
CI 的 Fetch config center 步骤拉取以上全部 key 注入环境变量,签名与上传步骤均不再依赖 Gitea secrets。
建议用 CONFIG_CENTER_TOKEN 为配置中心加鉴权(当前 REST API 无鉴权,任何人可读/写)。
本地构建 (可选, WSL)
bash scripts/build-apk.sh debug # 或 release
# 脚本自动复用本机 Android SDK/NDK (~/android-sdk, NDK 27.2.x)
项目首页 (Landing)
/ 为公开的项目首页 (src/pages/Landing.tsx),响应式适配桌面与手机 web:
- 项目简介 + 功能特性 + 技术栈
- APK 下载: 按钮直链 R2 公共桶
https://pub-...r2.dev/data/android/wardrobe.apk(由 android-build.yml 上传),附版本号、复制链接与安装提示 - web 新版本提示: 启动时请求
/api/ota/manifest-web,有新构建号时底部浮出"刷新"横幅 - 登录后的应用首页迁移到
/home(底部导航同步更新)
部署: npm run build && OTA_PACKAGE_DIR=ota-package npm start,server 同时托管首页 dist 与 OTA 接口。
OTA 升级
参考 iboard,采用 tauri-plugin-hotswap 前端热替换方案(minisign 签名 + 下载 + 校验 + 原子替换),升级不经过应用市场。
原理
- Tauri 客户端: 启动时
notifyReady();检查更新走插件内置checkUpdate(),下载/校验/替换/回滚均由插件完成(公钥在src-tauri/tauri.conf.json→plugins.hotswap.pubkey,端点在plugins.hotswap.endpoint) - Web 浏览器: 只比对
/api/ota/manifest-web的build_id与当前BUILD_ID,提示刷新(Vite 指纹会缓存失效) - 构建号
__BUILD_ID__(YYYYMMDD.HHmmss)由vite.config.ts每次构建注入
服务端 (server/)
| 接口 | 说明 |
|---|---|
GET /api/ota/check/:seq |
有新包 → 200 + manifest;无 → 204 |
GET /api/ota/bundle/:file |
下载 tar.gz 升级包 (流式) |
GET /api/ota/manifest-web |
Web 简化版 manifest |
GET /api/health |
健康检查 |
数据源: 环境变量 OTA_PACKAGE_DIR(默认 ota-package/)下的 latest.json + wardrobe-ota.tar.gz。
PORT 默认 3002(与 iboard 的 3001 同机共存)。可选 OTA_PUBLIC_BASE 让客户端直连 R2 下载 bundle。
本地生成 OTA 包
npm run build # 先构建 dist
minisign -G -W -p ota-package/hotswap.pub -s ota-package/hotswap.key # 或 npm run ota:genkey
npm run build:ota -- --notes "v0.2 更新说明" # 打包 + 签名 + 写 latest.json
npm start # 起服务, 手动验收 /api/ota/*
首次需安装 minisign(Windows: scoop/choco 或官方 win64 二进制)。
生成的 hotswap.key 请存到配置中心 service-wardrobe / ota / MINISIGN_PRIVATE_KEY(不要提交 git),
hotswap.pub 的公钥行写入 src-tauri/tauri.conf.json。
OTA 发布 CI (.gitea/workflows/ota-build.yml)
手动 workflow_dispatch 触发:
build frontend → npm run build
→ apt install minisign
→ 从配置中心拉取 MINISIGN_PRIVATE_KEY (service-wardrobe/ota) + CF_API_TOKEN (cloudflare)
→ node scripts/build-ota.mjs 打包+签名
→ scripts/upload-ota-r2.sh 上传到 R2 (store/ota/wardrobe-ota.tar.gz + latest.json)
客户端升级操作
「我的」页 → 「检查更新 (OTA)」:
- 检查更新 / 立即升级(带进度)/ 回滚(仅 OTA 覆盖时显示)
- Web 端仅可检查新构建号提示刷新
mise 本地工具链
本项目用 mise 统一管理 Node/npm/Rust 版本,仓库根目录 .mise.toml 声明工具版本,cd 进入目录后自动激活。
[tools]
node = "24.21.0"
npm = "12.0.2"
rust = "stable"
Windows 安装 (已完成)
- 下载
mise-v*-windows-x64.zip→ 解压到~\.local\bin\mise.exe - 用户 PATH 追加:
~\.local\bin与~\AppData\Local\mise\shims - PowerShell
$PROFILE添加:& "$env:USERPROFILE\.local\bin\mise.exe" activate powershell mise use -g node@24.21.0 npm@12.0.2 rust@stable安装全局工具
注意: Windows 下 mise 数据目录在
~\AppData\Local\mise,shims 位于~\AppData\Local\mise\shims(非~\.local\share\mise\shims)。
原型
浏览器打开 prototype/index.html 查看可交互原型。
License
Private