diff --git a/.gitignore b/.gitignore index 7b9412f..fd5651b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ logs/ # WorkBuddy tool state (not project code) .workbuddy/ +# Local dev tooling config (not project code) +.claude/ + # --- Python / Flask backend --- __pycache__/ *.py[cod] diff --git a/CLAUDE.md b/CLAUDE.md index 3b6dc22..963fac8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,88 +1,138 @@ -# Garmin Health Lab - 项目指南 +# Garmin Health Lab — 项目指南 -## 项目简介 +佳明(Garmin)健康数据分析平台:同步 Garmin Connect 数据,做多维度健康分析、 +可视化与 AI 解读(晨报 / 趋势归因 / Copilot)。 -Garmin Health Lab 是一个完整的健康数据分析平台,用于: -- 获取和同步佳明(Garmin)设备数据 -- 进行多维度的健康数据分析 -- 提供个性化的健康建议 -- 可视化健康趋势 +> **阅读顺序**:先读本文件(协作约定与现状速览)→ [PROGRESS.md](PROGRESS.md) +> (功能与部署进度,**排查前必读**)→ [README.md](README.md)(API 与使用)→ +> [docs/](docs/)(架构 / 开发 / 需求)。判断"部署在哪里、跑没跑、什么版本"时 +> 一律以 `PROGRESS.md` 和 `deploy/` 脚本为准,**不要凭记忆断言**。 -## 技术栈 +## 技术栈(现状,2026-09) -- **前端**: React 18 + TypeScript + Recharts -- **后端**: Node.js + Express + TypeScript -- **数据库**: SQLite3 -- **API 集成**: Garmin Connect API +- **后端**:Python 3.10+ / Flask(应用工厂),Gunicorn 生产运行。原 Node/Express + 后端早已重写为 Python,仓库中**不存在 `server/` 目录**。 +- **前端**:React 18 + TypeScript,用 **CRA(react-scripts)**构建,UI 组件库为 + **Framework7 9**(`framework7-react`,iOS 主题)+ Recharts 图表。根目录 + `package.json` 是 npm workspaces(含 `client`)。 +- **数据库**:可插拔数据层(`backend/db.py`),`DB_TYPE` 切换—— + 本地开发 SQLite(`backend/data/health.db`,默认);生产 NAS MariaDB + (`garmin_health_lab` 库,连接配置见 NAS 上 `backend/.env`)。 +- **认证**:**auth-hub SSO**(OAuth2/OIDC,`services/auth_hub_client.py`), + 本地邮箱密码注册/登录已移除;应用内 JWT 由后端签发。 +- **AI**:自建 ai-gateway(OpenAI 兼容,`https://ai.zichuan.xyz/v1`)为唯一上游 + (NVIDIA 推理模型,单次生成可达 2~5 分钟);生产 `AI_MODEL_CHAIN=gateway`。 -## 快速开始 +## 目录结构 -### 安装 -```bash -npm install ``` - -### 配置 -```bash -cp server/.env.example server/.env -# 编辑 server/.env 填入 Garmin 凭证 +GarminHealthLab/ +├── backend/ # Flask 后端(当前唯一后端) +│ ├── app.py # 应用工厂:装配蓝图 + 静态 UI + scheduler/jobs +│ ├── wsgi.py # Gunicorn 入口 +│ ├── config.py # 集中配置(读 backend/.env) +│ ├── auth.py # JWT 签发/校验 + require_auth +│ ├── db.py # 可插拔数据层(SQLite ↔ MariaDB)+ 幂等迁移 +│ ├── routes/ # API 蓝图(blueprint) +│ │ ├── auth.py # /api/auth auth-hub SSO: callback / logout / refresh / auth-hub/start +│ │ ├── garmin.py # /api/garmin 绑定/同步/MFA/退出/活动详情/同步详情 +│ │ ├── health.py # /api/health 摘要/睡眠/心率/运动/身体成分/血氧/设备/挑战等 +│ │ ├── analysis.py # /api/analysis 趋势/建议/briefing/trend-insight/copilot/insight +│ │ └── settings.py # /api/settings 个人资料/单位/同步频率/评分依据 +│ ├── services/ # 业务逻辑(纯函数/无 Flask 依赖,可单测) +│ │ ├── garmin.py # Garmin API 同步核心(限流退避 24h 写 DB) +│ │ ├── garmin_auth.py # Garmin OAuth 登录 + MFA 会话 +│ │ ├── garmin_extras.py # 身体成分/血压/血氧/设备/徽章等扩展数据 +│ │ ├── scheduler.py # 后台自动同步(DB 锁,多 worker 只跑一次) +│ │ ├── ai.py / coach.py / insights.py / jobs.py # AI 教练 + 生产者/消费者队列 +│ │ ├── analysis.py / health.py / fitness_age.py / settings.py +│ │ └── auth_hub_client.py / scopes.py +│ ├── data/ # SQLite(本地开发,git 忽略) +│ ├── static/ # 前端构建产物(生产由 Flask 同端口提供) +│ ├── tests/ # pytest 测试(446+ 项) +│ └── .env.example # 环境变量样例(真实 .env 不提交) +├── client/ # React 前端(CRA + Framework7) +│ ├── src/pages/ # 今日/健康/趋势/运动/设置/每日/睡眠/指标详情等页面 +│ ├── src/components/ # AI 晨报 / Copilot 浮窗 / 趋势归因 / Screen 骨架 +│ ├── src/lib/ # day.ts(本地日期)/ metrics.ts(指标注册表) +│ ├── src/features.ts # 功能开关(FEATURES.ai) +│ └── .env.production # REACT_APP_API_URL=/api(同源,防 Network Error) +├── deploy/ # NAS 部署脚本族(S99garmin/start/stop/push/deploy) +├── docs/ # 架构 / 开发 / 需求文档 +├── PROGRESS.md # 进度与部署事实(排查必读) +└── README.md # 项目说明 + API 文档 ``` -### 开发 -```bash -npm run dev -``` - -- 前端: http://localhost:3000 -- 后端: http://localhost:5000 - -## 项目结构 - -- `client/` - React 前端应用 -- `server/` - Node.js 后端服务 -- `docs/` - 文档 - -详见 [ARCHITECTURE.md](docs/ARCHITECTURE.md) 和 [DEVELOPMENT.md](docs/DEVELOPMENT.md) - -## 核心功能 - -1. **仪表板** - 健康数据概览和最近数据展示 -2. **数据同步** - 从 Garmin 同步最新健康数据 -3. **数据分析** - 趋势分析和数据可视化 -4. **健康建议** - 基于数据的个性化建议 -5. **设置** - 用户配置和偏好设置 - -## 关键特性 - -- ✅ Garmin API 集成 -- ✅ 多维度数据分析(步数、心率、睡眠等) -- ✅ 实时数据同步 -- ✅ 交互式数据可视化 -- ✅ JWT 认证安全 -- ✅ 本地数据存储 - -## 开发指南 - -- 详见 [DEVELOPMENT.md](docs/DEVELOPMENT.md) -- API 文档见 README.md - ## 常用命令 ```bash -npm run dev # 开发模式 -npm run build # 构建项目 -npm run typecheck # 类型检查 -npm start # 生产模式 +# 后端依赖(首次):在 backend/ 下建 .venv 并装 requirements-dev.txt +npm run setup:backend + +# 同时起前后端开发(backend:5000 由 BACKEND_PORT 固定;client CRA 代理到 5000) +npm run dev + +# 单独起后端 / 前端 / 测试 / 类型检查 / 构建 +npm run dev:backend +npm run dev:client +npm run test # backend/.venv/bin/python -m pytest(446+ 项) +npm run typecheck +npm run build # client/react-scripts build → client/build/ + +# 冒烟测试(后端单测入口) +cd backend && .venv/bin/python tests/smoke.py ``` -## 下一步任务 +## 部署(生产 = NAS,端口 8124) -- [ ] 实现 Garmin OAuth 认证 -- [ ] 完成 Garmin API 数据获取 -- [ ] 实现仪表板可视化 -- [ ] 添加数据分析算法 -- [ ] 部署和优化 +- **位置**:NAS `192.168.50.64` `/volume1/web/garmin-health-lab`,root 用户跑 + gunicorn `0.0.0.0:8124`(2 workers / 4 threads / --timeout 300),开机自启走 + DSM 任务调度器执行 `deploy/S99garmin.sh`。 +- **一键部署**:本地 `./deploy/push.sh`(tar 经 ssh 同步 backend + deploy + + 清空重推 client/build + sudo 重启,**校验 gunicorn pid 变化 + health 200**)。 + 前置:先 `npm run build`。 +- **公网**:NAS frpc → 甲骨文 `http://129.146.26.249:8124`(frp 重连需几秒)。 +- **DB**:NAS MariaDB 10.11,root 经 socket `/run/mysqld/mysqld10.sock`(或 + TCP 127.0.0.1:3306),库 `garmin_health_lab`。10.11 dump 含 `/*M!999999` + 注释、旧客户端会报错,且须 `--default-character-set=utf8mb4` 防中文丢失。 +- **auth-hub**:client `996aLPw4T5gl-rYZ`;回调注册了 LAN + `http://192.168.50.64:8124/auth/callback` 与公网 + `http://129.146.26.249:8124/auth/callback` 两个地址。 +- **部署/排障前**:先读 `PROGRESS.md` 与 `deploy/` 脚本确认事实(曾经凭旧记忆 + 断言"无线上环境"而误判)。服务以 root 运行:重启用 sudo,日志 + `logs/error.log`、`logs/access.log` 是 root 所有。 -## 联系方式 +## 关键约定与坑(写代码/改样式前看) -项目维护: ericwyuan.g@gmail.com +1. **日期一律本地日历日期**,别用 `toISOString()`(UTC,UTC+8 每天前 8 小时 + 查的是昨天)。前端统一走 `client/src/lib/day.ts`。 +2. **阈值全部来自公开参考值,AI 只解读不定阈值**(设计原则,勿破)。 +3. **Framework7 全局 `button { width: 100% }`** 会把任何 `