# 公网部署设计文档 **日期:** 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 有效期 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)