Files
DDUp/docs/plans/2026-01-20-public-deployment-design.md

363 lines
11 KiB
Markdown
Raw Normal View History

2026-01-22 12:57:26 +08:00
# 公网部署设计文档
**日期:** 2026-01-20
**状态:** 已确认,待实现
---
## 概述
为 Vitals 健康管理应用添加公网访问能力,包括用户认证、邀请码注册、管理后台,并通过 Docker 容器部署到云服务器。
---
## 需求总结
- **访问方式**:公网访问,通过 IP 地址(无域名)
- **认证方式**用户名密码登录JWT Token 认证
- **注册方式**:邀请码注册,邀请码由管理员生成
- **权限模型**:普通用户 + 管理员
- **部署方式**Docker 容器
---
## 角色权限
| 功能 | 普通用户 | 管理员 |
|------|---------|--------|
| 查看/编辑自己的数据 | ✅ | ✅ |
| 查看/编辑所有用户数据 | ❌ | ✅ |
| 管理用户(禁用/删除) | ❌ | ✅ |
| 生成邀请码 | ❌ | ✅ |
---
## 数据模型
### 用户表改动 (users)
新增字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| password_hash | TEXT | 密码哈希bcrypt |
| email | TEXT | 邮箱(可选) |
| is_admin | BOOLEAN | 是否管理员,默认 false |
| is_disabled | BOOLEAN | 是否禁用,默认 false |
### 新增邀请码表 (invites)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INTEGER | 主键,自增 |
| code | TEXT | 邀请码8位随机字符串 |
| created_by | INTEGER | 创建者 user_id |
| used_by | INTEGER | 使用者 user_idnull 表示未使用) |
| created_at | DATETIME | 创建时间 |
| expires_at | DATETIME | 过期时间(可选) |
---
## 认证方案
### JWT Token
- 登录成功后返回 token存储在浏览器 localStorage
- 每次请求在 Header 中携带 `Authorization: Bearer <token>`
- Token 有效期 7 天
- Token 内容:`{ user_id, username, is_admin, exp }`
### 密码存储
- 使用 bcrypt 哈希
- 不存储明文密码
---
## 页面设计
### 登录页 `/login`
```
┌─────────────────────────────────────┐
│ Vitals 登录 │
├─────────────────────────────────────┤
│ 用户名 │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ 密码 │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ [ 登录 ] │
│ │
│ 没有账号?立即注册 │
└─────────────────────────────────────┘
```
### 注册页 `/register`
```
┌─────────────────────────────────────┐
│ Vitals 注册 │
├─────────────────────────────────────┤
│ 邀请码 * │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ 用户名 * │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ 密码 * │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ 确认密码 * │
│ ┌─────────────────────────────┐ │
│ │ │ │
│ └─────────────────────────────┘ │
│ │
│ [ 注册 ] │
│ │
│ 已有账号?立即登录 │
└─────────────────────────────────────┘
```
### 管理后台 `/admin`(仅管理员可见)
```
┌─────────────────────────────────────┐
│ 导航栏(首页/运动/.../管理) │
├─────────────────────────────────────┤
│ 管理后台 │
├─────────────────────────────────────┤
│ 用户管理 │
│ ┌─────────────────────────────┐ │
│ │ 用户名 │ 状态 │ 创建时间 │ 操作│ │
│ │ rocky │ 正常 │ 01-20 │禁用│ │
│ │ test │ 禁用 │ 01-19 │启用│ │
│ └─────────────────────────────┘ │
├─────────────────────────────────────┤
│ 邀请码管理 [ 生成邀请码 ] │
│ ┌─────────────────────────────┐ │
│ │ 邀请码 │ 状态 │ 使用者 │ │
│ │ ABC12345 │ 已用 │ rocky │ │
│ │ XYZ98765 │ 未用 │ - │ │
│ └─────────────────────────────┘ │
├─────────────────────────────────────┤
│ 数据浏览 │
│ 用户筛选:[ 全部用户 ▼ ] │
│ 数据类型:[ 全部类型 ▼ ] │
│ ┌─────────────────────────────┐ │
│ │ 数据列表... │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
```
---
## API 设计
### 新增认证端点
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/auth/login` | POST | 登录,返回 JWT |
| `/api/auth/register` | POST | 注册(需邀请码) |
| `/api/auth/me` | GET | 获取当前用户信息 |
### 新增管理端点
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/admin/users` | GET | 用户列表 |
| `/api/admin/users/{id}` | DELETE | 删除用户 |
| `/api/admin/users/{id}/disable` | POST | 禁用用户 |
| `/api/admin/users/{id}/enable` | POST | 启用用户 |
| `/api/admin/invites` | GET | 邀请码列表 |
| `/api/admin/invites` | POST | 生成邀请码 |
| `/api/admin/data` | GET | 查看所有数据(支持筛选) |
### 现有 API 改动
- 所有 `/api/*` 端点添加 JWT 认证中间件
- 未登录返回 `401 Unauthorized`
- 数据自动按当前用户 `user_id` 过滤
- 排除列表:`/api/auth/login``/api/auth/register`
---
## 访问流程
```
用户访问任意页面
检查 localStorage 是否有 Token
无 Token → 跳转 /login
有 Token → 验证 Token 有效性
Token 无效/过期 → 跳转 /login
Token 有效 → 正常显示页面
```
---
## Docker 部署
### 文件结构
```
vitals/
├── Dockerfile
├── docker-compose.yml
├── .env.example
└── data/
└── vitals.db
```
### Dockerfile
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY pyproject.toml .
COPY src/ src/
RUN pip install --no-cache-dir -e .
EXPOSE 8080
CMD ["python", "-m", "vitals.web.app", "--host", "0.0.0.0"]
```
### docker-compose.yml
```yaml
version: '3.8'
services:
vitals:
build: .
ports:
- "8080:8080"
volumes:
- ./data:/app/data
environment:
- ADMIN_USERNAME=${ADMIN_USERNAME}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY}
- JWT_SECRET=${JWT_SECRET}
restart: unless-stopped
```
### .env.example
```env
# 管理员账户(首次启动时创建)
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change_me_please
# JWT 密钥(请使用随机字符串)
JWT_SECRET=your_random_secret_key_here
# AI 食物识别(可选)
DASHSCOPE_API_KEY=your_key_here
```
### 部署步骤
```bash
# 1. 上传代码到服务器
scp -r vitals/ user@server:/path/to/
# 2. SSH 到服务器
ssh user@server
# 3. 进入项目目录
cd /path/to/vitals
# 4. 复制并编辑环境变量
cp .env.example .env
vim .env
# 5. 启动容器
docker-compose up -d
# 6. 查看日志
docker-compose logs -f
```
访问:`http://服务器IP:8080`
---
## 实现步骤
1. **数据库改动**
- users 表添加 `password_hash``is_admin``is_disabled` 字段
- 新增 `invites`
2. **认证模块**
- 实现 JWT 生成/验证PyJWT
- 实现密码哈希bcrypt
- 添加认证中间件
3. **认证 API**
- 实现 `/api/auth/login`
- 实现 `/api/auth/register`
- 实现 `/api/auth/me`
4. **管理 API**
- 实现 `/api/admin/*` 端点
- 添加管理员权限检查
5. **前端页面**
- 登录页 `/login`
- 注册页 `/register`
- 管理后台 `/admin`
- 修改现有页面添加 Token 检查
6. **Docker 配置**
- 创建 Dockerfile
- 创建 docker-compose.yml
- 创建 .env.example
7. **首次启动逻辑**
- 检查环境变量创建管理员账户
8. **测试**
- 本地 Docker 测试
- 部署到服务器验证
---
## 技术选型
| 组件 | 选择 |
|------|------|
| 认证 | JWT (PyJWT) |
| 密码哈希 | bcrypt |
| 容器 | Docker + docker-compose |
| Web 服务器 | Uvicorn内置 |
---
## 安全考虑
- 密码使用 bcrypt 哈希存储
- JWT 设置合理过期时间7 天)
- 邀请码一次性使用
- 管理员操作需二次确认
- 建议后续添加 HTTPS通过 nginx 反向代理 + Let's Encrypt