11 KiB
11 KiB
公网部署设计文档
日期: 2026-01-20 状态: 已确认,待实现
概述
为 Vitals 健康管理应用添加公网访问能力,包括用户认证、邀请码注册、管理后台,并通过 Docker 容器部署到云服务器。
需求总结
- 访问方式:公网访问,通过 IP 地址(无域名)
- 认证方式:用户名密码登录,JWT Token 认证
- 注册方式:邀请码注册,邀请码由管理员生成
- 权限模型:普通用户 + 管理员
- 部署方式:Docker 容器
角色权限
| 功能 | 普通用户 | 管理员 |
|---|---|---|
| 查看/编辑自己的数据 | ✅ | ✅ |
| 查看/编辑所有用户数据 | ❌ | ✅ |
| 管理用户(禁用/删除) | ❌ | ✅ |
| 生成邀请码 | ❌ | ✅ |
数据模型
用户表改动 (users)
新增字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| password_hash | TEXT | 密码哈希(bcrypt) |
| 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
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
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
# 管理员账户(首次启动时创建)
ADMIN_USERNAME=admin
ADMIN_PASSWORD=change_me_please
# JWT 密钥(请使用随机字符串)
JWT_SECRET=your_random_secret_key_here
# AI 食物识别(可选)
DASHSCOPE_API_KEY=your_key_here
部署步骤
# 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
实现步骤
-
数据库改动
- users 表添加
password_hash、is_admin、is_disabled字段 - 新增
invites表
- users 表添加
-
认证模块
- 实现 JWT 生成/验证(PyJWT)
- 实现密码哈希(bcrypt)
- 添加认证中间件
-
认证 API
- 实现
/api/auth/login - 实现
/api/auth/register - 实现
/api/auth/me
- 实现
-
管理 API
- 实现
/api/admin/*端点 - 添加管理员权限检查
- 实现
-
前端页面
- 登录页
/login - 注册页
/register - 管理后台
/admin - 修改现有页面添加 Token 检查
- 登录页
-
Docker 配置
- 创建 Dockerfile
- 创建 docker-compose.yml
- 创建 .env.example
-
首次启动逻辑
- 检查环境变量创建管理员账户
-
测试
- 本地 Docker 测试
- 部署到服务器验证
技术选型
| 组件 | 选择 |
|---|---|
| 认证 | JWT (PyJWT) |
| 密码哈希 | bcrypt |
| 容器 | Docker + docker-compose |
| Web 服务器 | Uvicorn(内置) |
安全考虑
- 密码使用 bcrypt 哈希存储
- JWT 设置合理过期时间(7 天)
- 邀请码一次性使用
- 管理员操作需二次确认
- 建议后续添加 HTTPS(通过 nginx 反向代理 + Let's Encrypt)