Files
GarminHealthLab/CLAUDE.md
ericwyuan 2d9a2be185 docs: 文档/配置/测试同步 NAS :8124 生产现状,清除 Node.js 与甲骨文残留
背景:文档停在 Node.js 时代或甲骨文 8123 部署,与生产(NAS :8124 + Flask +
auth-hub + ai-gateway)严重脱节,曾导致凭旧记忆误判'无线上环境'。

- CLAUDE.md 重写:技术栈/结构/命令/部署事实/关键坑(F7 button、UTC 日期、
  429 退避以 DB 为准、迁移幂等、AI 生成耗时)
- docs/ARCHITECTURE.md 重写为 Flask 蓝图+services+可插拔数据层 + NAS 部署
- docs/DEVELOPMENT.md 重写为 Flask/CRA 开发指南 + push.sh 部署流程
- docs/REQUIREMENTS.md:部署条目改 NAS 8124;补 auth-hub/AI 教练/新修复
- docs/AUTH_HUB_INTEGRATION.md 新增(补 .env.example 悬空引用)
- README.md:技术栈/DB/auth-hub/API 清单/部署节修正
- backend/config.py 与 .env.example:AUTH_HUB_REDIRECT_URI 默认 8123→8124,
  MariaDB 注释 Oracle→NAS
- tests:GatewayCourtesy 并发测试对齐 MAX_CONCURRENT(AI_JOB_CONCURRENCY=2);
  conftest 禁用 create_app 后台队列线程,修整库测试 flaky(585 passed)
2026-09-02 19:34:32 +08:00

8.5 KiB
Raw Blame History

Garmin Health Lab — 项目指南

佳明Garmin健康数据分析平台同步 Garmin Connect 数据,做多维度健康分析、 可视化与 AI 解读(晨报 / 趋势归因 / Copilot

阅读顺序:先读本文件(协作约定与现状速览)→ PROGRESS.md (功能与部署进度,排查前必读)→ README.mdAPI 与使用)→ docs/(架构 / 开发 / 需求)。判断"部署在哪里、跑没跑、什么版本"时 一律以 PROGRESS.mddeploy/ 脚本为准,不要凭记忆断言

技术栈现状2026-09

  • 后端Python 3.10+ / Flask应用工厂Gunicorn 生产运行。原 Node/Express 后端早已重写为 Python仓库中不存在 server/ 目录
  • 前端React 18 + TypeScript用 **CRAreact-scripts**构建UI 组件库为 Framework7 9framework7-reactiOS 主题)+ Recharts 图表。根目录 package.json 是 npm workspacesclient)。
  • 数据库:可插拔数据层(backend/db.pyDB_TYPE 切换—— 本地开发 SQLitebackend/data/health.db,默认);生产 NAS MariaDB garmin_health_lab 库,连接配置见 NAS 上 backend/.env)。
  • 认证auth-hub SSOOAuth2/OIDCservices/auth_hub_client.py 本地邮箱密码注册/登录已移除;应用内 JWT 由后端签发。
  • AI:自建 ai-gatewayOpenAI 兼容,https://ai.zichuan.xyz/v1)为唯一上游 NVIDIA 推理模型,单次生成可达 2~5 分钟);生产 AI_MODEL_CHAIN=gateway

目录结构

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 文档

常用命令

# 后端依赖(首次):在 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 pytest446+ 项)
npm run typecheck
npm run build           # client/react-scripts build → client/build/

# 冒烟测试(后端单测入口)
cd backend && .venv/bin/python tests/smoke.py

部署(生产 = NAS端口 8124

  • 位置NAS 192.168.50.64 /volume1/web/garmin-health-labroot 用户跑 gunicorn 0.0.0.0:81242 workers / 4 threads / --timeout 300开机自启走 DSM 任务调度器执行 deploy/S99garmin.sh
  • 一键部署:本地 ./deploy/push.shtar 经 ssh 同步 backend + deploy + 清空重推 client/build + sudo 重启,校验 gunicorn pid 变化 + health 200)。 前置:先 npm run build
  • 公网NAS frpc → 甲骨文 http://129.146.26.249:8124frp 重连需几秒)。
  • DBNAS MariaDB 10.11root 经 socket /run/mysqld/mysqld10.sock(或 TCP 127.0.0.1:3306garmin_health_lab。10.11 dump 含 /*M!999999 注释、旧客户端会报错,且须 --default-character-set=utf8mb4 防中文丢失。
  • auth-hubclient 996aLPw4T5gl-rYZ;回调注册了 LAN http://192.168.50.64:8124/auth/callback 与公网 http://129.146.26.249:8124/auth/callback 两个地址。
  • 部署/排障前:先读 PROGRESS.mddeploy/ 脚本确认事实(曾经凭旧记忆 断言"无线上环境"而误判)。服务以 root 运行:重启用 sudo日志 logs/error.loglogs/access.log 是 root 所有。

关键约定与坑(写代码/改样式前看)

  1. 日期一律本地日历日期,别用 toISOString()UTCUTC+8 每天前 8 小时 查的是昨天)。前端统一走 client/src/lib/day.ts
  2. 阈值全部来自公开参考值AI 只解读不定阈值(设计原则,勿破)。
  3. Framework7 全局 button { width: 100% } 会把任何 <button> 拉满父容器 ——自定义组件里的按钮要显式 width: autoCopilot 浮窗 ✕/发送按钮曾因此 错位,见 Copilot.css 注释。F7 .navbar .left/.right 的 frosted pill 也 需在 f7theme.css 覆盖。
  4. 同步限流Garmin 账号 429 退避 24hrate_limited_untilDB 为准 (多 worker 内存不一致会卡死守卫);_is_rate_limited 沿异常 __cause__ 链识别 RetryError 里的 429。
  5. AI 生成慢:网关上游推理模型单次 2~5 分钟,晨报走后台生成 + 轮询, 接口超时/流式不可靠见 services/ai.py 的非流式回退;生产队列并发 AI_JOB_CONCURRENCY=2(共享网关,太高会把别家打成 502
  6. 迁移幂等db.pyinit_db() 会被多 worker 并发调用,所有 ALTER/CREATE 必须幂等(CREATE TABLE IF NOT EXISTS + 先查列再加列)。
  7. 不要提交.env、构建产物 client/build、SQLite 数据、.workbuddy/.claude/(已在 .gitignore。改代码后同步 backend/static/ 用 deploy 流程。
  8. 一任务一 commit,完成即推送 NAS Giteaorigin 就是 Gitea

测试

  • pytest446+ 项)覆盖:设置吸附/校验、身体年龄、运动详情列存解析、Garmin 同步入库与只读本地保证、AI 缓存/队列、调度器等。改后端先跑 npm run test(或 cd backend && .venv/bin/python -m pytest)。
  • 前端无单测框架,用 Chrome headless + CDP 截图做界面回归(见每日日志)。

下一步 / 已知问题

  • PROGRESS.md 的待办区与 docs/REQUIREMENTS.md
  • 已知独立 bugNAS logs/stdout.logrefill_backlog ... SQL syntax near 'TEXT)) AND NOT EXISTS' 反复刷——某条 AI 回填 SQL 与 NAS MariaDB 语法对 不上,尚未修复。