Files
DDUp/docs/plans/2026-01-20-public-deployment-design.md
2026-01-22 12:57:26 +08:00

11 KiB
Raw Permalink Blame History

公网部署设计文档

日期: 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

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


实现步骤

  1. 数据库改动

    • users 表添加 password_hashis_adminis_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