363 lines
11 KiB
Markdown
363 lines
11 KiB
Markdown
|
|
# 公网部署设计文档
|
|||
|
|
|
|||
|
|
**日期:** 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_id(null 表示未使用) |
|
|||
|
|
| 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)
|