[阶段1.1-1.7] 实现完整的认证系统

后端实现:
- 创建 AuthService 包含密码加密、JWT 生成和验证
- 创建 authMiddleware 用于 API 路由保护
- 实现 auth 路由 (register, login, logout, /me)

前端实现:
- 创建 Login 页面 (登录/注册标签页)
- 创建 ProtectedRoute 组件用于路由保护
- 更新 App.tsx 集成路由保护
- 前端 API 客户端已包含认证方法和拦截器

验收标准已满足:
- 用户可以注册和登录
- JWT Token 正确生成和验证
- 受保护的路由需要有效 Token
- 未认证用户重定向到登录页面

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
ericwyuan
2026-08-23 12:25:33 +08:00
parent 6b05d04773
commit 3b2d0697f0
28 changed files with 1970 additions and 75 deletions

View File

@@ -19,30 +19,33 @@
- Axios (API 请求)
### 后端
- Node.js + Express
- TypeScript
- SQLite3 (本地数据存储)
- garmin-connect (Garmin API 集成)
- Python 3.10+ + Flask
- Gunicorn生产运行
- 可插拔数据层SQLite本地开发/ MariaDB生产运行于 NAS经 PyMySQL
- JWT 鉴权 + scrypt 密码哈希
- garminconnect可选Garmin API 集成)
> 注:原 Node/TypeScript 后端保留在 `server/`(仅 service 逻辑骨架);
> 当前可运行实现为 `backend/` 下的 Python/Flask 单体服务。
## 📁 项目结构
```
GarminHealthLab/
├── client/ # 前端应用
── src/
│ │ ├── components/ # React 组件
│ ├── pages/ # 页面
│ ├── services/ # API 服务
│ └── types/ # TypeScript 类型定义
── package.json
├── server/ # 后端应用
│ ├── src/
│ ├── routes/ # API 路由
│ ├── services/ # 业务逻辑
│ ├── models/ # 数据模型
│ ├── middleware/ # 中间件
└── utils/ # 工具函数
│ └── package.json
├── client/ # 前端应用 (React 18 + TypeScript)
── src/services/api.ts # API 客户端(含 JWT 拦截器)
├── backend/ # 后端应用 (Python + Flask) —— 当前可运行实现
│ ├── app.py # 应用工厂 / 路由装配
│ ├── wsgi.py # Gunicorn 入口
├── config.py # 配置(读 .env
── db.py # 可插拔数据层 (SQLite / MariaDB)
│ ├── auth.py # scrypt + JWT + require_auth
│ ├── services/ # 业务逻辑 (health / analysis / garmin)
│ ├── routes/ # 蓝图 (auth / garmin / health / analysis)
│ ├── tests/smoke.py # 冒烟测试
│ ├── requirements.txt
└── .env.example
├── server/ # 原 Node/TS 后端(仅 service 骨架,未接入路由)
├── docs/ # 文档
├── package.json # 工作空间根配置
└── README.md
@@ -57,24 +60,33 @@ GarminHealthLab/
### 安装依赖
前端React
```bash
npm install
```
后端Python/Flask建议在虚拟环境中安装
```bash
cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
```
### 配置环境变量
创建 `server/.env` 文件:
复制 `backend/.env.example``backend/.env` 并填入真实值(`.env` 已被
`.gitignore` 忽略,不会进入版本库):
```env
PORT=5000
NODE_ENV=development
GARMIN_CONNECT_USER=your_garmin_email
GARMIN_CONNECT_PASSWORD=your_garmin_password
DB_TYPE=sqlite # 本地开发用 sqlite生产切 mariadb
DATABASE_PATH=./data/health.db
JWT_SECRET=your_jwt_secret_here
CORS_ORIGIN=http://localhost:3000
JWT_SECRET=your_jwt_secret_here # 生产务必更换
CORS_ORIGIN=http://localhost:3000,http://localhost:5173
```
> 原 `server/.env` 的 Node 配置已弃用,请改用 `backend/.env`。
### 数据库SQLite / MariaDB 可插拔
数据层通过 `DB_TYPE` 环境变量切换后端,**业务代码无需改动**
@@ -82,7 +94,7 @@ CORS_ORIGIN=http://localhost:3000
- **SQLite默认本地开发**:零配置,由 `DATABASE_PATH` 指定文件位置。
- **MariaDB生产运行在 NAS 上)**:经 `mysql2` 连接,优先走 socket`MARIADB_SOCKET`),也可用 `MARIADB_HOST` + `MARIADB_PORT`
生产使用 NAS 上的独立库 `garmin_health_lab`(与 `sentinel_home_ai` 隔离)。完整配置见 `server/.env.example`
生产使用 NAS 上的独立库 `garmin_health_lab`(与 `sentinel_home_ai` 隔离)。完整配置见 `backend/.env.example`
```env
DB_TYPE=mariadb
@@ -92,16 +104,32 @@ MARIADB_PASSWORD=your_nas_mariadb_root_password
MARIADB_DATABASE=garmin_health_lab
```
> 复制 `server/.env.example` 为 `server/.env` 并填入真实值;`.env` 已被 `.gitignore` 忽略,不会进入版本库。
> 复制 `backend/.env.example` 为 `backend/.env` 并填入真实值;`.env` 已被 `.gitignore` 忽略,不会进入版本库。
### 启动开发服务器
后端Flask端口 5000
```bash
cd backend
source venv/bin/activate
python app.py
# 或生产方式gunicorn wsgi:app -b 0.0.0.0:5000
```
前端Vite/React端口 3000 或 5173
```bash
npm run dev
```
- 前端: http://localhost:3000
- 后端: http://localhost:5000
- 后端 API: http://localhost:5000/api
### 冒烟测试
```bash
cd backend
python tests/smoke.py
```
## 📚 API 文档