内容
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/Vitals-Personal_Health_Dashboard-F5A623?style=for-the-badge&labelColor=1a1a2e">
<img alt="Vitals" src="https://img.shields.io/badge/Vitals-Personal_Health_Dashboard-F5A623?style=for-the-badge&labelColor=1a1a2e">
</picture>
</p>
<p align="center">
<strong>自托管健康数据湖,适用于 Claude.ai 的 25 个 MCP 工具</strong><br>
<sub>OAuth 2.0 · 12 个健康领域 · 体重 · GLP-1 · Garmin · Hevy · 营养 · 实验室 · 基因 · 护肤 · AI 摘要</sub>
</p>
<p align="center">
<img src="https://img.shields.io/github/stars/ilodezis/vitals?style=flat-square" alt="Stars">
<img src="https://img.shields.io/github/last-commit/ilodezis/vitals?style=flat-square" alt="Last Commit">
<img src="https://img.shields.io/badge/Python-3.11-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python">
<img src="https://img.shields.io/badge/FastAPI-0.115+-009688?style=flat-square&logo=fastapi&logoColor=white" alt="FastAPI">
<img src="https://img.shields.io/badge/PostgreSQL-15-4169E1?style=flat-square&logo=postgresql&logoColor=white" alt="PostgreSQL">
<img src="https://img.shields.io/badge/Redis-7-DC382D?style=flat-square&logo=redis&logoColor=white" alt="Redis">
<img src="https://img.shields.io/badge/Docker-Compose-2496ED?style=flat-square&logo=docker&logoColor=white" alt="Docker">
<img src="https://img.shields.io/badge/HTMX-1.9-3D72D7?style=flat-square" alt="HTMX">
<img src="https://img.shields.io/badge/Alpine.js-3.x-8BC0D0?style=flat-square&logo=alpinedotjs&logoColor=white" alt="Alpine.js">
<img src="https://img.shields.io/badge/Tailwind-3.0-38BDF8?style=flat-square&logo=tailwindcss&logoColor=white" alt="Tailwind">
<img src="https://img.shields.io/badge/MCP-Claude_AI-D4A574?style=flat-square" alt="MCP">
<img src="https://img.shields.io/badge/PWA-Mobile_First-5A0FC8?style=flat-square&logo=pwa&logoColor=white" alt="PWA">
<img src="https://img.shields.io/badge/License-PolyForm_NC-green?style=flat-square" alt="License">
</p>
<p align="center">
<img src="./gifs/MCP_en.gif" alt="Claude querying Vitals via MCP" width="100%">
</p>
<p align="center">
<a href="#vitals---个人健康仪表盘"><img src="https://raw.githubusercontent.com/lipis/flag-icons/main/flags/4x3/cn.svg" width="20" valign="middle" alt="cn"> 中文</a> ·
<a href="#vitals---personal-health-dashboard"><img src="https://raw.githubusercontent.com/lipis/flag-icons/main/flags/4x3/us.svg" width="20" valign="middle" alt="en"> English</a>
</p>
---
## Vitals - 个人健康仪表盘
**Vitals** 是一个个人健康仪表盘和完整的数据湖(data lake),适用于单个用户。该系统旨在长期跟踪生物标志物、身体重组、GLP-1 治疗监测、运动表现分析和通过语言模型生成每周 AI 报告。
与健身追踪器的主要区别在于 **原始数据最大程度保留** 的原则。Vitals 是一个智能健康导航器,突出显示睡眠、锻炼、药物和体重之间的不明显关联,帮助做出明智的决定。不是监督者,而是领航员。
### 为什么我创建了它
我的数据分散在六个应用程序中:Garmin、Hevy、GLP-1 笔记、分析表格、补充剂文件。没有一个能将睡眠与锻炼后的恢复联系起来,或将治疗的副作用与营养联系起来——所有这些都需要靠记忆。
Vitals 使用 Claude 作为主要开发工具,但数据模型、架构和领域结构是我设计的。MCP 层是关键部分:Claude 可以实时读写 12 个领域的所有数据,并回答诸如“过去一周的睡眠如何影响恢复?”之类的问题,并提供具体数字。
### 功能
<p align="center">
<img src="./gifs/dashboard_en.gif" alt="Vitals dashboard walkthrough" width="100%">
</p>
<table>
<tr>
<td width="50%">
**📊 数据与分析**
- 12 个独立的健康领域
- 自动同步 Garmin Connect + Hevy API
- 通过 LLM 进行 OCR 解析
- 补充剂和基因冲突引擎
- 7 天滚动平均值 + 线性回归
- GLP-1 平台检测
</td>
<td width="50%">
**🤖 AI 与集成**
- 每周 AI 摘要(Claude / GPT)
- 适用于 Claude.ai 的 25 个 MCP 工具(读 + 写)
- OAuth 2.0 + PKCE 授权 MCP
- LLM 数据完整导出(可复制粘贴)
- 原子备份和数据库恢复
- 通过 OpenRouter 自定义模型
</td>
</tr>
<tr>
<td>
**📱 移动体验**
- 可安装到主屏幕的 PWA
- 适用于 iPhone / Android 的适配
- HTMX - 无需重新加载的即时转换
- Alpine.js - 客户端微交互
- Chart.js - 自适应图表
</td>
<td>
**🔒 隐私与控制**
- 自托管,单用户,VPN 后
- Bcrypt 授权 + 签名会话
- CSRF + 源保护 + CSP
- 模块化仪表盘 - 启用/禁用领域
- 所有数据在您的服务器上
</td>
</tr>
</table>
---
### 目录
- [哲学与关键原则](#-哲学与关键原则)
- [数据领域(12 个模块)](#-数据领域)
- [系统架构](#-系统架构)
- [与 Claude.ai 的 MCP 集成(25 个工具)](#-mcp-集成与-claudeai)
- [快速入门(Docker Compose)](#-快速入门)
- [安全部署(作者设置)](#-安全部署)
- [配置参数(.env)](#-配置参数)
- [开发与测试](#-开发与测试)
- [支持项目](#-支持项目)
- [许可证](#-许可证)
---
### 哲学与关键原则
> [!NOTE]
> **1. 原始数据存储**
> 所有集成都将原始 API 响应保存在 `raw_payloads` 的 JSONB 表中,与结构化记录并行。如果解析器明天更新,整个历史记录都将被重新解析而不会丢失。
> [!TIP]
> **2. 冲突引擎**
> 代替硬验证器 - 软和硬警告规则(`conflict_rules`)。不兼容的补充剂或突然的体重增加将返回 `409 Conflict`。您可以点击“无论如何都记录”(Override)- 原因将被记录在日志中。
> [!IMPORTANT]
> **3. 本地化时间**
> “今天”的时间和日期范围严格在用户的时区内(`VITALS_TIMEZONE`)。晚上锻炼时没有混乱的图表。所有时间都经过 `vitals/utils/timeutils`。
> [!CAUTION]
> **4. 自托管和单用户隐私**
> 生物识别数据仅属于您。 在 VPN 后部署到您自己的服务器。 会话 cookie、Bcrypt 密码、CSRF 保护。
> [!TIP]
> **5. 完整的数据可移植性**
> 系统不会锁定数据。 数据库的完整备份(包括原始 API 响应)用于迁移。 紧凑的文本导出(LLM-ready)用于粘贴到 Claude/ChatGPT 聊天中。 原子恢复来自备份的单个事务。
---
### 数据领域
所有 12 个领域都通过公共接口 `InsightsMixin`(`date`、`domain`、`source` + 复合索引)联合:
<details>
<summary><strong>⚖️ 1. 体重和身体成分</strong></summary>
- **模型**: `WeightLog`, `BodyMeasurement`, `NoiseMarker`, `ProgressPhoto`
- 使用海军公式(颈部、腰部、臀部)和无脂肪质量(LBM)计算体脂百分比
- 7 天滚动平均值,排除噪声周期(`NoiseMarker`)
- 线性回归:每周公斤数和目标日期预测
- 进度照片与日期绑定(批量加载最多 5 张)
- 手动体重覆盖 Garmin 相同日期的导入(Garmin → `superseded`)
</details>
<details>
<summary><strong>💉 2. GLP-1 协议</strong></summary>
- **模型**: `Injection`, `DosePhase`, `SideEffect`
- 注射的按时间顺序日志(Semaglutide / Tirzepatide)带有交互式点图
- 用于绘制体重图的剂量阶段线性比例
- 平台检测:如果剂量 > 14 天且趋势 < 100 g/周 → 自动警告
</details>
<details>
<summary><strong>⌚ 3. Garmin Connect</strong></summary>
- **模型**: `GarminDaily`, `GarminActivity`
- 自动同步:睡眠、心率变异性、静息心率、压力、身体电池、训练准备度
- Garth 会话:Redis 中的令牌 + 磁盘备份 → 最少授权
- 备用通道:从 Health Auto Export 导入 JSON 导出
</details>
<details>
<summary><strong>🏋️ 4. Hevy 训练</strong></summary>
- **模型**: `HevyWorkout`, `HevyExercise`, `HevySet`
- 通过 API 同步力量训练(锻炼、重量、组数、重复次数)
- 将训练量与 Garmin 恢复指标关联
</details>
<details>
<summary><strong>🍏 5. 营养</strong></summary>
- **模型**: `MealLog`
- 记录具有卡路里和宏量营养素(蛋白质/脂肪/碳水化合物)的膳食
- 精确的进食时间(`eaten_at`)和文本备注
- 通过 `.env` 自定义基于 KBJU 的目标,并可视化进度
</details>
<details>
<summary><strong>💊 6. 补充剂目录</strong></summary>
- **模型**: `Supplement`
- 补充剂目录:剂量、服用时间、证据等级(A/B/C)
- 通过冲突引擎检查与基因和其他补充剂的冲突
</details>
<details>
<summary><strong>🧴 7. 护肤</strong></summary>
- **模型**: `SkincareProduct`, `SkincareLog`, `SkincareObservation`
- 早晚护肤日记,记录活性成分(酸、维A醇)
- 记录皮肤状况(油性、干燥、痘痘)
</details>
<details>
<summary><strong>🧪 8. 实验室和 OCR</strong></summary>
- **模型**: `LabResult`, `LabMarker`
- 加载 PDF/照片分析 → LLM 解析生物标志物(推荐 `gemini-2.5-flash`)
- 计算偏差(低/高/严重)和指标变化图表
</details>
<details>
<summary><strong>🧬 9. 基因(VCF)</strong></summary>
- **模型**: `GeneticVariant`
- VCF 文件解析器 → 将 SNP 与易感性目录匹配
- 冲突引擎的补充剂分配数据源
</details>
<details>
<summary><strong>🎯 10. 目标和里程碑</strong></summary>
- **模型**: `Milestone`
- 具有目标值和截止日期的交互式卡片
- 动态计算完成百分比和剩余时间预测
</details>
<details>
<summary><strong>🤖 11. AI 摘要</strong></summary>
- **模型**: `WeeklyDigest`
- 每个领域的每周 LLM 报告(推荐 `claude-sonnet-4.6`)
- 关注关联:*“HRV 因 Hevy 训练量而下降,在 GLP-1 平台上”*
</details>
<details>
<summary><strong>🔗 12. 与 Claude.ai 的 MCP 集成</strong></summary>
- `/mcp` 上的 FastMCP 服务器(SSE 传输)
- OAuth 2.0 + PKCE + 持有者令牌
- 25 种工具:14 种用于读取 + 11 种用于写入数据
- 更多信息 → [MCP 部分](#-mcp-集成与-claudeai)
</details>
---
### 🏗️ 系统架构
```mermaid
graph TD
Garmin[Garmin Connect / Garth] -->|自动同步| Raw[raw_payloads · JSONB]
Hevy[Hevy REST API] -->|自动同步| Raw
实验室[PDF 分析 / 照片] -->|LLM 解析| Raw
VCF[遗传 VCF] -->|导入| Raw
Raw --> 冲突引擎[Conflict Engine]
冲突引擎 --> 洞察层[InsightsMixin Layer]
调度器[APScheduler] -->|Redis 锁| 冲突引擎
洞察层 --> 数据库[(PostgreSQL 15)]
洞察层 --> 缓存[(Redis)]
数据库和缓存 --> FastAPI[FastAPI 路由器 + 认证 + CSRF]
FastAPI --> 界面[Jinja2 + HTMX + Alpine.js]
FastAPI --> MCP[FastMCP SSE 服务器]
界面 --> 图表[Chart.js]
界面 --> AI[AI 摘要 · LLM]
MCP <-->|OAuth 2.0 + PKCE| Claude[Claude.ai]
```
| 层 | 责任 |
| :--- | :--- |
| **`vitals/`**(核心) | 模型、服务、业务逻辑。不了解 FastAPI。被导入到脚本和测试中。 |
| **`web/`**(交付) | FastAPI 路由、身份验证、CSRF、Jinja2 模板。调用服务,不包含业务逻辑。 |
| **前端** | HTML-over-the-wire:HTMX + Alpine.js + Chart.js。PWA-first,移动设备适配。 |
---
### 与 Claude.ai 的 MCP 集成
内置 [FastMCP](https://github.com/jlowin/fastmcp) 服务器在 `/mcp` 上可用(SSE 传输)。身份验证:OAuth 2.0 + PKCE,带有 Bearer 令牌验证。在 Web 界面中配置(设置部分)。
**25 种工具** - Claude 可以完全读写所有领域的数据:
#### 读取(14 种工具)
| 工具 | 描述 |
| :--- | :--- |
| `get_user_profile` | 身体参数、目标、训练计划 |
| `get_weight_logs` | 体重记录、身体测量、噪音周期 |
| `get_glp1_logs` | GLP-1 注射、剂量阶段、副作用 |
| `get_garmin_metrics` | 心率变异性、睡眠、静息心率、活动 |
| `get_hevy_workouts` | 力量训练与组和重量 |
| `get_supplements_catalog` | 补充剂目录与剂量和证据水平 |
| `get_skincare_logs` | 护肤常规和皮肤状况日志 |
| `get_lab_results` | 生物标志物和参考值的实验室结果 |
| `get_genetics_snps` | 基因变异(rsid,基因) |
| `get_active_alerts` | 未解决的系统警报 |
| `get_weekly_digests` | AI 摘要存档 |
| `get_nutrition_summary` | 膳食总结(KBJU)和目标进度 |
| `get_measurements` | 身体测量(颈部、腰部、臀部、体脂百分比、LBM) |
| `get_notes` | 来自所有领域的笔记记录 |
#### 写入(11 种工具)
| 工具 | 描述 |
| :--- | :--- |
| `log_meal` | 记录具有卡路里和宏量营养素(蛋白质/脂肪/碳水化合物)的膳食 |
| `update_meal` | 更新现有的膳食记录 |
| `delete_meal` | 删除膳食记录 |
| `search_meals` | 按名称和/或日期搜索膳食 |
| `log_weight` | 记录体重(覆盖同一日期的 Garmin 导入) |
| `delete_weight` | 删除体重记录(重新激活下一个优先级) |
| `log_glp1` | 记录 GLP-1 注射(药物、剂量、位置) |
| `log_skincare` | 记录/更新日常护肤清单(upsert) |
| `log_measurement` | 记录身体测量(自动计算海军体脂百分比) |
| `log_note` | 添加任何领域的笔记记录 |
| `check_supplement_conflicts` | 检查补充剂与基因和其他补充剂的冲突 |
#### 与 Claude 的对话示例
```
用户:最近 2 周我的情况如何?体重、训练、睡眠 - 整体情况。
Claude:[调用 get_weight_logs、get_hevy_workouts、get_garmin_metrics]
体重:89.2 → 87.8 公斤,14 天内(-1.4 公斤,~0.7 公斤/周 - 计划范围内)。
训练:4 次训练(2 次上身,2 次腿部)。渐进超负荷:卧推 +5 公斤,腿部推举 +7.5 公斤。
睡眠:平均 7 小时 12 分钟,心率变异性在过去 3 天下降 - 可能是训练后累积疲劳。身体电池恢复正常。
建议:恢复足够,下一次训练可以进行。
```
---
### 🚀 快速入门
#### 1. 克隆并设置环境
```bash
git clone https://github.com/ilodezis/vitals.git
cd vitals
cp .env.example .env
```
#### 2. 生成密钥
```bash
# 用于签名会话 cookie 的密钥
python -c "import secrets; print(secrets.token_urlsafe(48))"
# 登录的 bcrypt 哈希密码
python -c "import bcrypt; print(bcrypt.hashpw(b'您的密码', bcrypt.gensalt(4)).decode())"
```
#### 3. 填写 `.env`
```env
VITALS_SESSION_SECRET="第一次命令的结果"
VITALS_AUTH_USERNAME="您的用户名"
VITALS_AUTH_PASSWORD_HASH="第二次命令的结果"
```
#### 4. 启动
```bash
docker compose up -d --build
```
> [!NOTE]
> `vitals_app` 启动时,Alembic 迁移会自动应用。PostgreSQL 15 位于 Docker 网络内。
#### 5. 检查
仪表盘:`http://127.0.0.1:8000`
```bash
curl -s http://127.0.0.1:8000/health
```
---
### 🛡️ 安全部署(作者设置)
由于 Vitals 包含敏感的医疗、基因和个人生物识别数据,**不建议将应用程序公开于互联网**。默认情况下,`docker-compose.yml` 中的端口仅绑定到本地地址 (`127.0.0.1:8000`),以便从外部无法直接访问。
#### 方案 A:Cloudflare Tunnel(推荐)
这是使用 SSL 证书发布应用程序的最简单、最可靠的方法,无需公开端口(您的服务器保持对端口扫描器的不可见性)。
1. **域名注册**:在 Cloudflare 中添加您的域名。
2. **创建隧道**:在 Cloudflare Zero Trust 面板中,转到 **Access -> Tunnels** 并创建新的隧道(例如 `vitals-tunnel`)。
3. **安装 cloudflared**:复制 Cloudflare 提供的安装命令(适用于您的操作系统,如 Ubuntu/Debian),并在您的 VPS 上执行。它将作为系统服务安装和启动 `cloudflared` 守护进程。
4. **路由**:在隧道设置中,转到 **Public Hostname** 选项卡并指定:
- **子域名/域名**:例如 `vitals.yourdomain.com`
- **服务类型**:`HTTP`
- **网址**:`localhost:8000`(或 `127.0.0.1:8000`)
5. **额外保护(Cloudflare Access)**:
- 为了在登录页面之前保护您的应用程序,请在 Zero Trust 面板中转到 **Access -> Applications** 并为您的域名(例如 `vitals.yourdomain.com`)创建应用程序。
- 配置允许仅您的电子邮件地址通过一次性 PIN 码或社交提供商(Google/GitHub)进行访问的访问策略。现在,在登录 Vitals 之前,您需要通过 Cloudflare 确认您的身份。
---
#### 选项 B: 经典 Nginx 反向代理 + SSL + VPN/Basic Auth
经典方法使用 Nginx 作为反向代理服务器。
1. **DNS 设置**: 将域名(例如 `vitals.yourdomain.com`)的 A 记录指向您的 VPS 公共 IP 地址。
2. **安装 Nginx**:
```bash
sudo apt update
sudo apt install nginx -y
```
3. **主机配置**: 创建文件 `/etc/nginx/sites-available/vitals`,内容如下:
```nginx
server {
listen 80;
server_name vitals.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 禁用上传文件大小限制
client_max_body_size 20M;
}
}
```
激活配置并重启 Nginx:
```bash
sudo ln -s /etc/nginx/sites-available/vitals /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl restart nginx
```
4. **安装 SSL 证书(Let's Encrypt)**:
```bash
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d vitals.yourdomain.com
```
5. **访问限制(可选)**:
- **通过 VPN(Tailscale/WireGuard)**: 配置 Nginx 监听内部 VPN 接口的端口 `443`(例如 `listen 100.x.x.x:443 ssl`),而非服务器外部接口。
- **通过 HTTP Basic Auth**: 保护路由并设置密码。生成密码文件:
```bash
sudo apt install apache2-utils -y
sudo htpasswd -c /etc/nginx/.htpasswd username
```
在 Nginx 配置的 `location /` 块中添加:
```nginx
auth_basic "Private Vitals Access";
auth_basic_user_file /etc/nginx/.htpasswd;
```
---
#### 选项 C: 直接通过域名(通过 Cloudflare 代理)
如果您有委托给 Cloudflare 的域名,并希望通过 Cloudflare 进行简单公开访问,且使用 SSL 而无需额外的 VPN 或 Cloudflare Zero Trust 授权。
1. **Cloudflare DNS 设置**:
- 创建 A 记录(例如 `vitals.yourdomain.com`),指向您的 VPS 公共 IP 地址。
- 确保启用代理模式(**Proxied** / 橙色云)。这将隐藏您的 VPS 真实 IP 地址,并提供内置的 DDoS 防护。
2. **Cloudflare SSL/TLS 设置**:
- 在 Cloudflare 控制面板中,转到 **SSL/TLS -> Overview**。
- 将加密模式设置为 **Full** 或 **Full (strict)**。
3. **在 VPS 上安装 Nginx**:
```bash
sudo apt update
sudo apt install nginx -y
```
4. **Nginx 配置**:
创建文件 `/etc/nginx/sites-available/vitals`,将流量代理到本地端口 8000:
```nginx
server {
listen 80;
server_name vitals.yourdomain.com;
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
激活配置并重启 Nginx:
```bash
sudo ln -s /etc/nginx/sites-available/vitals /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl restart nginx
```
5. **SSL 证书(选择一个)**:
- **Cloudflare 源站证书(推荐)**:
- 在 Cloudflare 中,转到 **SSL/TLS -> Origin Server** 并点击 **Create Certificate**。
- 复制私钥和证书内容并保存在 VPS 上(例如 `/etc/ssl/private/cloudflare.key` 和 `/etc/ssl/certs/cloudflare.pem`)。
- 更新 Nginx 配置以使用这些证书:
```nginx
server {
listen 80;
server_name vitals.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name vitals.yourdomain.com;
ssl_certificate /etc/ssl/certs/cloudflare.pem;
ssl_certificate_key /etc/ssl/private/cloudflare.key;
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
重启 Nginx:`sudo systemctl restart nginx`。
- **使用 Let's Encrypt**:
- 如果您不想使用 Cloudflare 源站证书,只需通过 Certbot 获取标准 Let's Encrypt 证书:
```bash
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d vitals.yourdomain.com
```
---
### ⚙️ 配置参数
<details>
<summary><strong>基础设施</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_DATABASE_URL` | PostgreSQL(asyncpg) | `postgresql+asyncpg://...` |
| `VITALS_REDIS_URL` | Redis 用于缓存和锁 | `redis://vitals_redis:6379/0` |
| `VITALS_TIMEZONE` | 用户时区 | `Europe/Chisinau` |
</details>
<details>
<summary><strong>授权和安全</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_SESSION_SECRET` | 会话签名密钥 | *必填* |
| `VITALS_AUTH_USERNAME` | 用户名 | *必填* |
| `VITALS_AUTH_PASSWORD_HASH` | 密码 Bcrypt 哈希 | *必填* |
| `VITALS_SESSION_TTL` | 会话超时时间(秒) | `2592000`(30 天) |
| `VITALS_COOKIE_SECURE` | Cookie 安全标志 | `true` |
| `VITALS_COOKIE_SAMESITE` | SameSite 策略 | `lax` |
</details>
<details>
<summary><strong>用户资料</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_HEIGHT_CM` | 身高(厘米) | `175` |
| `VITALS_SEX` | 性别(`male` / `female`) | `male` |
| `VITALS_USER_AGE` | 年龄 | `25` |
| `VITALS_USER_PROGRAM` | 程序描述 | *内置* |
| `VITALS_USER_GOALS` | 目标(逗号分隔) | `减脂,维持肌肉` |
</details>
<details>
<summary><strong>营养目标</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_NUTRITION_PROTEIN_TARGET_G` | 每日蛋白质目标(克) | `150` |
| `VITALS_NUTRITION_CALORIES_MIN` | 最低卡路里 | `1300` |
| `VITALS_NUTRITION_CALORIES_MAX` | 最高卡路里 | `1700` |
</details>
<details>
<summary><strong>集成和 AI</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_OPENROUTER_API_KEY` | OpenRouter API 密钥 | *可选* |
| `VITALS_LLM_MODEL_DIGEST` | 摘要模型 | `anthropic/claude-sonnet-4.6` |
| `VITALS_LLM_MODEL_PARSER` | OCR 分析模型 | `google/gemini-2.5-flash` |
| `VITALS_HEVY_API_KEY` | Hevy API 密钥 | *可选* |
| `VITALS_GARMIN_EMAIL` | Garmin Connect 邮箱 | *可选* |
| `VITALS_GARMIN_PASSWORD` | Garmin Connect 密码 | *可选* |
| `VITALS_GARMIN_TOKEN_DIR` | Garmin Token 目录 | `/data/garmin_session` |
| `VITALS_MCP_CLIENT_ID` | MCP OAuth Client ID | `vitals-claude-connector` |
| `VITALS_MCP_CLIENT_SECRET` | MCP OAuth Client Secret | *必填* |
</details>
---
### 🛠️ 开发和测试
#### 本地环境
```bash
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS/Linux
pip install -r requirements-dev.txt
```
#### 开发服务器(无 Docker)
```bash
python run_local.py
```
自动启动 SQLite + FakeRedis。默认登录信息:`timur` / `password`。地址:`http://127.0.0.1:8000`。
#### 演示数据
```bash
python scripts/seed_demo.py
```
在所有域(体重、Garmin、训练、营养、GLP-1、分析、补充剂、遗传学、皮肤护理)中填充本地数据库以提供合理的演示数据。可重复运行。
#### 测试
```bash
# 单元测试(SQLite,快速)
python -m pytest -q
# 集成测试(Postgres Docker)
bash scripts/test_postgres.sh
```
---
### ☕ 支持项目
> [!TIP]
> 如果 **Vitals** 帮助您跟踪健康指标并简化生活,您可以支持该项目:
>
> * **中国银行卡(CloudTips)**:[pay.cloudtips.ru/p/5bb82648](https://pay.cloudtips.ru/p/5bb82648)
> * **国际银行卡(Ko-fi)**:[ko-fi.com/ilodezis](https://ko-fi.com/ilodezis)
> * **USDT(TRC-20)**:`TV6bMburcEjgkinkZBvt9DonkyiaP7TqHG`
> * **USDT(Arbitrum One)**:`0x3eac15f5d07bba100d4038cad603e420a84753bb`
---
### 📄 许可证
**PolyForm Noncommercial License 1.0.0** - 允许非商业用途使用和修改。严禁商业使用。详情见:[LICENSE](LICENSE)。
---
---
## Vitals - 个人健康仪表盘
**Vitals** 是一个为单一用户设计的个人健康仪表盘和数据湖。适用于长期跟踪生物标志物、身体重组、GLP-1 治疗、运动表现和 AI 驱动的每周分析摘要(通过 OpenRouter LLMs)。
与典型的健身追踪器不同,Vitals 优先**保留原始历史数据**。它作为一个智能健康导航器 - 揭示睡眠、锻炼、补充剂和身体成分之间的关联。不是监督 - 而是副驾驶。
### 为什么我创建了它
我的数据分散在六个应用程序中:Garmin、Hevy、GLP-1 笔记、一个包含实验室结果的电子表格、一个包含补充剂的文件。没有一个能将睡眠与训练恢复交叉引用,或将治疗副作用与营养联系起来 - 我不得不把所有这些都记在脑海中。
使用 Claude 作为主要编码工具构建 - 但数据模型、架构和领域设计都是我自己的。MCP 层是关键部分:Claude 可以实时读写所有 12 个领域的数据,回答诸如“这周我的睡眠如何影响我的恢复?”之类的问题,并提供实际数字。
<p align="center">
<img src="./gifs/MCP_en.gif" alt="Claude querying Vitals via MCP" width="100%">
</p>
### 它能做什么
<p align="center">
<img src="./gifs/dashboard_en.gif" alt="Vitals dashboard walkthrough" width="100%">
</p>
<table>
<tr>
<td width="50%">
**📊 数据与分析**
- 12 个独立的健康领域
- 自动同步 Garmin Connect + Hevy API
- 通过 LLM 进行 OCR 实验室报告解析
- 补充剂与遗传学的冲突引擎
- 7 天移动平均值 + 线性回归
- GLP-1 平台检测
</td>
<td width="50%">
**🤖 AI 与集成**
- 每周 AI 摘要(通过 OpenRouter 的 Claude / GPT)
- 适用于 Claude.ai 的 25 个 MCP 工具(读 + 写)
- MCP 的 OAuth 2.0 + PKCE 授权
- LLM 的完整数据导出(复制粘贴就绪)
- 原子数据库备份与恢复
- 通过 OpenRouter 配置模型
</td>
</tr>
<tr>
<td>
**📱 移动体验**
- 具有首页安装的 PWA
- 适用于 iPhone / Android 的适配版本
- HTMX - 即时转换,无需重新加载页面
- Alpine.js - 客户端微交互
- Chart.js - 响应式图表
</td>
<td>
**🔒 隐私与控制**
- 自托管,单用户,VPN 后
- Bcrypt 认证 + 签名会话 Cookie
- CSRF + 源守护 + CSP
- 模块化仪表盘 - 可切换域
- 所有数据保留在您的服务器上
</td>
</tr>
</table>
---
### 📖 目录
- [核心哲学](#-核心哲学)
- [支持的领域(12 个模块)](#-支持的领域)
- [技术架构](#-技术架构)
- [MCP 与 Claude.ai 集成(25 个工具)](#-mcp-integration-with-claudeai)
- [快速入门(Docker Compose)](#-quick-start)
- [安全部署(创建者设置)](#-secure-deployment-creators-setup)
- [配置(.env)](#-configuration)
- [开发与测试](#-development--testing)
- [支持项目](#-support-the-project)
- [许可证](#-license)
---
### 🧠 核心哲学
> [!NOTE]
> **1. 原始数据保留**
> 所有 API 集成在规范化记录的同时,将原始 JSON 有效载荷保存在 JSONB `raw_payloads` 表中。如果解析器更改 - 整个历史记录可以重新处理而不会丢失任何数据。
> [!TIP]
> **2. 弹性冲突引擎**
> 而不是硬验证 - 软和硬警告规则(`conflict_rules`)。补充剂与遗传学冲突或异常体重激增返回 `409 Conflict`。用户可以显式覆盖并添加审计备注。
> [!IMPORTANT]
> **3. 本地化时间边界**
> 每日重置和日历数学运算根据用户的时区(`VITALS_TIMEZONE`)进行,而不是服务器时钟。不会因深夜锻炼而出现分段日子。所有时间通过 `vitals/utils/timeutils` 进行。
> [!CAUTION]
> **4. 单用户自托管隐私**
> 您的生物特征数据属于您。设计用于在 VPN 后部署。安全的会话 Cookie、Bcrypt 密码、CSRF 保护。
> [!TIP]
> **5. 端到端数据可移植性**
> 完整的数据库备份(包括原始 API 有效载荷)用于迁移。精选的、没有秘密的文本导出,设计用于粘贴到 LLM 聊天中(Claude、ChatGPT)。在单个数据库事务中原子恢复。
---
### 📊 支持的领域
所有 12 个领域共享 `InsightsMixin` 接口(`date`、`domain`、`source` + 复合索引):
<details>
<summary><strong>⚖️ 1. 体重与身体成分</strong></summary>
- **模型**:`WeightLog`、`BodyMeasurement`、`NoiseMarker`、`ProgressPhoto`
- 海军体脂百分比公式(颈、腰、臀)+ 精瘦体质量(LBM)
- 7天移动平均值,排除噪声期(`NoiseMarker`)
- 线性回归:每周速率(千克/周)+ 目标日期预测
- 进步照片与日历链接(批量上传最多5张)
- 手动日志覆盖Garmin导入相同日期的记录(Garmin → `superseded`)
</details>
<details>
<summary><strong>💉 2. GLP-1 方案</strong></summary>
- **模型**:`Injection`、`DosePhase`、`SideEffect`
- 按时间顺序的注射日志(Semaglutide / Tirzepatide),带有交互式站点地图
- 剂量阶段在体重图表上叠加以进行相关性分析
- 平台检测:剂量 > 14 天 + 趋势 < 100g/周 → 自动警告
</details>
<details>
<summary><strong>⌚ 3. Garmin Connect</strong></summary>
- **模型**:`GarminDaily`、`GarminActivity`
- 自动同步:睡眠、心率变异性、静息心率、压力、身体电池、训练准备度
- Garth 会话令牌缓存在 Redis + 磁盘备份 → 最小化重新身份验证
- 备用:直接健康自动导出 JSON 导入
- 最后同步时间戳显示
</details>
<details>
<summary><strong>🏋️ 4. Hevy 训练</strong></summary>
- **模型**:`HevyWorkout`、`HevyExercise`、`HevySet`
- API 同步:锻炼、组数、次数、重量
- 交叉参考训练量与 Garmin 恢复指标
- 最后同步时间戳显示
</details>
<details>
<summary><strong>🍏 5. 营养</strong></summary>
- **模型**:`MealLog`
- 记录餐食热量和宏量营养素(蛋白质、脂肪、碳水化合物)
- 时钟时间(`eaten_at`)+ 文本备注
- 通过 `.env` 配置可调的宏量营养素目标,仪表盘进度表显示
- 自动包含在每周 AI 摘要中
</details>
<details>
<summary><strong>💊 6. 补充剂目录</strong></summary>
- **模型**:`Supplement`
- 具有剂量、时间、证据等级(A/B/C)的补充剂数据库
- 通过冲突引擎检查与遗传学和并发物质的冲突
</details>
<details>
<summary><strong>🧴 7. 皮肤护理日志</strong></summary>
- **模型**:`SkincareProduct`、`SkincareLog`、`SkincareObservation`
- 早晚日常日志,包含产品活性物质(维A醇、去角质)
- 皮肤状态跟踪(保湿、油腻、痘痘)
- 自动警告冲突活性物质
</details>
<details>
<summary><strong>🧪 8. 实验室结果与 OCR</strong></summary>
- **模型**:`LabResult`、`LabMarker`
- 上传 PDF / 图片 → LLM 解析生物标志物(推荐:`gemini-2.5-flash`)
- 范围外标记(低 / 高 / 临界)+ 交互式历史图表
</details>
<details>
<summary><strong>🧬 9. 遗传学(VCF)</strong></summary>
- **模型**:`GeneticVariant`
- VCF 文件解析器 → 健康相关的 SNP(咖啡因、铁、乳糖)
- 馈入冲突引擎以进行补充剂安全性检查
</details>
<details>
<summary><strong>🎯 10. 里程碑与目标</strong></summary>
- **模型**:`Milestone`
- 具有数字目标和截止日期的交互式目标卡片
- 基于活动体重指标的实时进度百分比
</details>
<details>
<summary><strong>🤖 11. 每周 AI 摘要</strong></summary>
- **模型**:`WeeklyDigest`
- 跨所有领域的每周 LLM 摘要(推荐:`claude-sonnet-4.6`)
- 关注因果联系:*“HRV 在训练量减少后恢复,突破了当前 GLP-1 阶段的体重停滞”*
</details>
<details>
<summary><strong>🔗 12. Claude.ai MCP 集成</strong></summary>
- `/mcp` 上的快速 MCP 服务器(SSE 传输)
- OAuth 2.0 + PKCE + 持有者令牌
- 25 个工具:14 个读取 + 11 个写入
- 详情 → [MCP 部分](#-mcp-integration-with-claudeai)
</details>
---
### 🏗️ 技术架构
```mermaid
graph TD
Garmin[佳明运动 / Garth] -->|自动同步| Raw[原始数据 · JSONB]
Hevy[Hevy REST API] -->|自动同步| Raw
Labs[实验室PDF / 图片] -->|LLM解析| Raw
VCF[遗传学 VCF] -->|导入| Raw
Raw --> 冲突引擎[冲突引擎]
冲突引擎 --> 洞察层[洞察层]
调度器[APScheduler] -->|Redis锁| 冲突引擎
洞察层 --> 数据库[(PostgreSQL 15)]
洞察层 --> 缓存[(Redis)]
数据库 & 缓存 --> FastAPI[FastAPI 路由 + 认证 + CSRF]
FastAPI --> UI[模板 + HTMX + Alpine.js]
FastAPI --> MCP[FastMCP SSE 服务器]
UI --> 图表[Chart.js]
UI --> AI[AI摘要 · LLM]
MCP <-->|OAuth 2.0 + PKCE| Claude[Claude.ai]
```
| 层 | 责任 |
| :--- | :--- |
| **`vitals/`** (核心) | 模型、服务、业务逻辑。零Web依赖。可在脚本和测试中导入。 |
| **`web/`** (交付) | FastAPI路由、认证、CSRF、Jinja2模板。调用服务 — 包含无业务逻辑。 |
| **前端** | HTML-over-the-wire: HTMX + Alpine.js + Chart.js。PWA优先,移动适配。 |
---
### 🔗 MCP 与 Claude.ai 集成
内置 [FastMCP](https://github.com/jlowin/fastmcp) 服务器位于 `/mcp`(SSE传输)。授权:OAuth 2.0 + PKCE,带有签名Bearer令牌验证。在Web仪表盘设置中配置。
**25种工具** — Claude可以全面读写所有领域的数据:
#### 读取(14种工具)
| 工具 | 描述 |
| :--- | :--- |
| `get_user_profile` | 身体概况、目标、训练计划 |
| `get_weight_logs` | 体重历史、身体测量、噪音标记 |
| `get_glp1_logs` | GLP-1注射、剂量阶段、副作用 |
| `get_garmin_metrics` | HRV、睡眠、静息心率、活动 |
| `get_hevy_workouts` | 力量训练,包含组数、次数、重量 |
| `get_supplements_catalog` | 补充剂,包含剂量和证据等级 |
| `get_skincare_logs` | 皮肤护理日志和皮肤观察 |
| `get_lab_results` | 实验室报告,包含生物标志物和参考范围 |
| `get_genetics_snps` | 基因SNP(rsid,基因) |
| `get_active_alerts` | 未解决的警告和冲突通知 |
| `get_weekly_digests` | 历史AI摘要存档 |
| `get_nutrition_summary` | 每日营养总结(卡路里,宏量营养素)和目标进度 |
| `get_measurements` | 身体测量(颈围、腰围、臀围、体脂百分比、瘦体重) |
| `get_notes` | 各领域记录的备注 |
#### 写入(11种工具)
| 工具 | 描述 |
| :--- | :--- |
| `log_meal` | 记录一餐,包含卡路里和宏量营养素 |
| `update_meal` | 更新现有的餐记录 |
| `delete_meal` | 删除餐记录 |
| `search_meals` | 按名称和/或日期搜索餐 |
| `log_weight` | 记录体重(覆盖同一日期的佳明数据) |
| `delete_weight` | 删除体重记录(重新激活下一个优先级) |
| `log_glp1` | 记录GLP-1注射(药物、剂量、部位) |
| `log_skincare` | 记录/更新每日皮肤护理检查清单(插入/更新) |
| `log_measurement` | 记录身体测量(自动计算海军体脂百分比) |
| `log_note` | 添加备注到任意领域记录 |
| `check_supplement_conflicts` | 检查补充剂与基因和活跃补充剂的冲突 |
#### 示例交互
```
用户:总结我最近2周的情况 — 体重趋势、锻炼、睡眠质量。
Claude:[调用 get_weight_logs、get_hevy_workouts、get_garmin_metrics]
体重:89.2 → 87.8 公斤,14天内(-1.4 公斤,约0.7 公斤/周 — 按计划进行)。
锻炼:完成4次训练(2次上肢,2次下肢)。渐进超负荷:
卧推 +5 公斤,拉力器下拉 +7.5 公斤。
睡眠:平均 7小时12分钟,HRV在过去3天呈下降趋势 — 可能累积了训练后的疲劳。身体电池恢复正常。
建议:恢复情况足够,下一训练 session 可以进行。
```
---
### 🚀 快速开始
#### 1. 克隆和配置
```bash
git clone https://github.com/ilodezis/vitals.git
cd vitals
cp .env.example .env
```
#### 2. 生成密钥
```bash
# 会话cookie签名密钥
python -c "import secrets; print(secrets.token_urlsafe(48))"
# Bcrypt密码哈希
python -c "import bcrypt; print(bcrypt.hashpw(b'your-password', bcrypt.gensalt(4)).decode())"
```
#### 3. 更新 `.env`
```env
VITALS_SESSION_SECRET="your-session-secret"
VITALS_AUTH_USERNAME="your-username"
VITALS_AUTH_PASSWORD_HASH="your-bcrypt-hash"
```
#### 4. 启动
```bash
docker compose up -d --build
```
> [!NOTE]
> Alembic迁移在`vitals_app`启动时自动运行。PostgreSQL 15在Docker网络内运行。
#### 5. 验证
仪表盘:`http://127.0.0.1:8000`
```bash
curl -s http://127.0.0.1:8000/health
```
---
### 🛡️ 安全部署(创建者设置)
由于Vitals存储敏感的医疗史、基因特征和个人生物测量数据,**强烈建议不要将应用程序暴露在公共互联网上**。默认情况下,`docker-compose.yml`中的端口映射仅绑定到环回接口(`127.0.0.1:8000`),使应用程序对外部流量不可见。
项目创建者将Vitals部署在**Ubuntu VPS**上,并使用以下三种方法之一确保安全访问:
#### 选项A:Cloudflare隧道(推荐)
这是发布应用程序并使用有效SSL证书的最简单、最安全的方法,无需在防火墙或路由器上暴露任何端口(您的服务器IP地址保持隐藏)。
1. **添加域名**:在Cloudflare中设置您的域名。
2. **创建隧道**:在 [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/) 中,导航到 **Access -> Tunnels** 并创建一个新隧道(例如,`vitals-tunnel`)。
3. **安装cloudflared**:复制Cloudflare为您的服务器操作系统(Ubuntu/Debian)提供的安装命令,并在VPS上运行。这将`cloudflared`守护程序安装为系统服务。
4. **路由流量**:在隧道的 **Public Hostname** 选项卡的配置下,添加:
- **子域名/域名**:例如,`vitals.yourdomain.com`
- **服务类型**:`HTTP`
- **URL**:`localhost:8000`(或 `127.0.0.1:8000`)
5. **额外保护(Cloudflare Access)**:
- 为了在用户看到Vitals登录屏幕之前确保应用程序的安全,请转到 **Access -> Applications** 并为您的域名(例如,`vitals.yourdomain.com`)添加自托管应用程序。
- 定义一个Access Policy,仅允许通过一次性PIN(OTP)或Google/GitHub等身份验证提供者访问您的电子邮件地址。
---
#### 选项B:经典Nginx反向代理 + SSL + VPN/基本认证
使用Nginx作为反向代理的传统设置。
1. **DNS设置**:将您的域名的A记录(例如,`vitals.yourdomain.com`)指向您的VPS的公共IP地址。
2. **安装Nginx**:
```bash
sudo apt update
sudo apt install nginx -y
```
3. **配置虚拟主机**:在 `/etc/nginx/sites-available/vitals` 中创建文件,包含以下配置:
```nginx
server {
listen 80;
server_name vitals.yourdomain.com;
# 允许上传较大的实验室报告和照片
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
激活配置并重启Nginx:
```bash
sudo ln -s /etc/nginx/sites-available/vitals /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl restart nginx
```
4. **配置SSL证书(Let's Encrypt)**:
```bash
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d vitals.yourdomain.com
```
5. **限制访问(选择一种)**:
- **通过VPN(Tailscale/WireGuard)**:配置Nginx仅在您的私有VPN IP接口(例如,`listen 100.x.x.x:443 ssl`)上监听SSL流量,而不是公共接口。
- **通过HTTP基本认证**:生成密码文件:
```bash
sudo apt install apache2-utils -y
sudo htpasswd -c /etc/nginx/.htpasswd username
```
在Nginx的`location /`块中添加以下指令:
```nginx
auth_basic "Private Vitals Access";
auth_basic_user_file /etc/nginx/.htpasswd;
```
---
#### 选项C:简单公共域名(通过Cloudflare代理)
如果您想使用Cloudflare管理的自定义域名直接公开访问Vitals仪表盘,并使用Cloudflare的边缘服务器处理SSL。
1. **Cloudflare DNS设置**:
- 添加一个A记录(例如,`vitals.yourdomain.com`)指向您的VPS的公共IP地址。
- 确保代理状态设置为 **Proxied** (橙色云)。这隐藏了您的VPS的公共IP地址,并保护它免受直接流量/DDoS攻击。
2. **Cloudflare SSL/TLS设置**:
- 在Cloudflare仪表盘中,转到 **SSL/TLS -> Overview**。
- 将加密模式更改为 **Full** 或 **Full (strict)**。
3. **在VPS上安装Nginx**:
```bash
sudo apt update
sudo apt install nginx -y
```
4. **配置虚拟主机**:
在 `/etc/nginx/sites-available/vitals` 中创建配置文件,将流量转发到本地端口8000:
```nginx
server {
listen 80;
server_name vitals.yourdomain.com;
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
激活配置并重启Nginx:
```bash
sudo ln -s /etc/nginx/sites-available/vitals /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl restart nginx
```
5. **SSL证书(选择一种)**:
- **Cloudflare源服务器证书(推荐)**:
- 在Cloudflare仪表盘中,转到 **SSL/TLS -> Origin Server** 并点击 **Create Certificate**。
- 复制证书和私钥,并将它们保存在您的VPS上(例如,`/etc/ssl/certs/cloudflare.pem` 和 `/etc/ssl/private/cloudflare.key`)。
- 更新您的Nginx配置文件,以监听带有这些证书的443端口:
```nginx
server {
listen 80;
server_name vitals.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name vitals.yourdomain.com;
ssl_certificate /etc/ssl/certs/cloudflare.pem;
ssl_certificate_key /etc/ssl/private/cloudflare.key;
client_max_body_size 20M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
重启Nginx:`sudo systemctl restart nginx`。
- **Let's Encrypt / Certbot**:
- 或者,获取标准的Let's Encrypt证书:
```bash
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d vitals.yourdomain.com
```
---
### ⚙️ 配置
<details>
<summary><strong>基础设施</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_DATABASE_URL` | PostgreSQL(asyncpg) | `postgresql+asyncpg://...` |
| `VITALS_REDIS_URL` | Redis用于缓存和锁 | `redis://vitals_redis:6379/0` |
| `VITALS_TIMEZONE` | 用户时区 | `Europe/Chisinau` |
</details>
<details>
<summary><strong>认证和安全</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_SESSION_SECRET` | 会话cookie签名密钥 | *必填* |
| `VITALS_AUTH_USERNAME` | 仪表盘用户名 | *必填* |
| `VITALS_AUTH_PASSWORD_HASH` | Bcrypt密码哈希 | *必填* |
| `VITALS_SESSION_TTL` | 会话时长(秒) | `2592000`(30天) |
| `VITALS_COOKIE_SECURE` | 安全cookie标志 | `true` |
| `VITALS_COOKIE_SAMESITE` | SameSite策略 | `lax` |
</details>
<details>
<summary><strong>用户资料</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_HEIGHT_CM` | 身高(厘米) | `175` |
| `VITALS_SEX` | 性别(`male` / `female`) | `male` |
| `VITALS_USER_AGE` | 年龄 | `25` |
| `VITALS_USER_PROGRAM` | 程序描述 | *嵌入式* |
| `VITALS_USER_GOALS` | 目标(逗号分隔) | `减少脂肪,保留肌肉` |
</details>
<details>
<summary><strong>营养目标</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_NUTRITION_PROTEIN_TARGET_G` | 每日蛋白质目标(克) | `150` |
| `VITALS_NUTRITION_CALORIES_MIN` | 最低每日卡路里 | `1300` |
| `VITALS_NUTRITION_CALORIES_MAX` | 最高每日卡路里 | `1700` |
</details>
<details>
<summary><strong>集成与AI</strong></summary>
| 变量 | 描述 | 默认值 |
| :--- | :--- | :--- |
| `VITALS_OPENROUTER_API_KEY` | OpenRouter API密钥 | *可选* |
| `VITALS_LLM_MODEL_DIGEST` | 摘要模型 | `anthropic/claude-sonnet-4.6` |
| `VITALS_LLM_MODEL_PARSER` | 实验室OCR模型 | `google/gemini-2.5-flash` |
| `VITALS_HEVY_API_KEY` | Hevy API密钥 | *可选* |
| `VITALS_GARMIN_EMAIL` | 佳明运动邮箱 | *可选* |
| `VITALS_GARMIN_PASSWORD` | 佳明运动密码 | *可选* |
| `VITALS_GARMIN_TOKEN_DIR` | 佳明运动会话令牌路径 | `/data/garmin_session` |
| `VITALS_MCP_CLIENT_ID` | MCP OAuth客户端ID | `vitals-claude-connector` |
| `VITALS_MCP_CLIENT_SECRET` | MCP OAuth客户端密钥 | *必填* |
</details>
---
### 🛠️ 开发与测试
#### 本地环境
```bash
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS/Linux
pip install -r requirements-dev.txt
```
#### 开发服务器(无Docker)
```bash
python run_local.py
```
自动配置SQLite + FakeRedis。默认登录:`timur` / `password`。地址:`http://127.0.0.1:8000`。
#### 演示数据
```bash
python scripts/seed_demo.py
```
在所有领域(体重、佳明运动、锻炼、营养、GLP-1、实验室、补充剂、遗传学、皮肤护理)填充本地数据库以实现数据示例。幂等 — 安全地重新运行。
#### 测试
```bash
# 单元测试(SQLite,瞬间完成)
python -m pytest -q
# 集成测试(Postgres Docker)
bash scripts/test_postgres.sh
```
---
### ☕ 支持项目
> [!TIP]
> 如果 **Vitals** 帮助您跟踪健康指标并改善生活,请考虑支持其开发。贡献用于让创建者保持精力充沛:
>
> * **俄罗斯银行卡(CloudTips)**:[pay.cloudtips.ru/p/5bb82648](https://pay.cloudtips.ru/p/5bb82648)
> * **国际银行卡(Ko-fi)**:[ko-fi.com/ilodezis](https://ko-fi.com/ilodezis)
> * **USDT (TRC-20)**:`TV6bMburcEjgkinkZBvt9DonkyiaP7TqHG`
> * **USDT (Arbitrum One)**:`0x3eac15f5d07bba100d4038cad603e420a84753bb`
---
### 📄 许可
**PolyForm Noncommercial License 1.0.0** — 免费用于个人和私人用途。商业用途禁止。参见 [LICENSE](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器