- 响应式项目首页 (Landing, 含 R2 APK 下载) + 路由 /home 调整 - OTA 升级: tauri-plugin-hotswap (lib.rs/main.rs 拆分, 客户端热替换) - OTA 服务端 (check/bundle/manifest-web) + build-ota/genkey/upload 脚本 - 密钥/凭据统一存配置中心 (signing + ota + cloudflare 分组) - Android CI (android-* 分支) + OTA CI (手动触发), 产物上传 R2 - 修复 tsc/构建阻塞问题
264 lines
10 KiB
Markdown
264 lines
10 KiB
Markdown
# 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](https://nodejs.org) ≥ 18
|
||
- [Rust](https://rustup.rs) ≥ 1.77
|
||
- [Android Studio](https://developer.android.com/studio) (Android 构建)
|
||
|
||
### 开发
|
||
|
||
```bash
|
||
npm install
|
||
npm run tauri dev # 桌面端开发
|
||
```
|
||
|
||
### Android 构建
|
||
|
||
```bash
|
||
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.md)。通过 `mcp_list_tools` / `mcp_call_tool` IPC 命令暴露 10 个数据管理工具。
|
||
|
||
## Android CI/CD
|
||
|
||
参考 [D:\workbench\iboard](../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
|
||
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](../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 包
|
||
|
||
```bash
|
||
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](https://jedisct1.github.io/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](https://mise.jdx.dev) 统一管理 Node/npm/Rust 版本,仓库根目录 `.mise.toml` 声明工具版本,`cd` 进入目录后自动激活。
|
||
|
||
```toml
|
||
[tools]
|
||
node = "24.21.0"
|
||
npm = "12.0.2"
|
||
rust = "stable"
|
||
```
|
||
|
||
### Windows 安装 (已完成)
|
||
|
||
1. 下载 `mise-v*-windows-x64.zip` → 解压到 `~\.local\bin\mise.exe`
|
||
2. 用户 PATH 追加: `~\.local\bin` 与 `~\AppData\Local\mise\shims`
|
||
3. PowerShell `$PROFILE` 添加: `& "$env:USERPROFILE\.local\bin\mise.exe" activate powershell`
|
||
4. `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 |