7.4 KiB
7.4 KiB
阶段 2:SQLAlchemy + Alembic + 用户登录体系
1. 数据库与迁移
- 连接:backend 通过环境变量
DATABASE_URL(异步postgresql+asyncpg://...)连接 PostgreSQL;Alembic 使用DATABASE_URL_SYNC(同步postgresql://...)。 - 表结构:
- users:id (uuid)、username、password_hash、role、is_active、created_at
- audit_logs:id (uuid)、actor_user_id (FK users)、action、meta_json (JSONB)、created_at
迁移流程说明
Alembic 会按版本顺序执行 alembic/versions/ 下的迁移脚本,在数据库中创建或修改表,并在库中记录当前版本(表 alembic_version)。
执行时:
- 读取
backend/alembic.ini和backend/alembic/env.py。 env.py从app.config.settings读取database_url_sync(即环境变量DATABASE_URL_SYNC),用该同步连接串连接 PostgreSQL。- 对比数据库中的
alembic_version与本地迁移文件,将尚未执行的迁移按顺序执行(例如001_users_and_audit_logs.py会创建users、audit_logs表)。
方式 A:Docker 启动时自动迁移
步骤:
- 在项目根目录执行:
docker compose up -d - Compose 会先启动
db,等待健康检查通过后再启动backend。 - backend 容器的启动命令为:
sh -c "alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000" - 迁移阶段:
- 容器内当前目录为
/app(即 backend 代码根目录),PYTHONPATH=/app。 - 执行
alembic upgrade head时,Alembic 在/app下找到alembic.ini、alembic/env.py和alembic/versions/。 - 环境变量由
docker-compose注入(含DATABASE_URL_SYNC=postgresql://wecom:wecom_secret@db:5432/wecom_ai,注意主机名为db)。 env.py通过app.config.settings读到该连接串,用同步驱动连接 PostgreSQL,执行所有未执行的迁移(如创建users、audit_logs)。
- 容器内当前目录为
- 应用启动:迁移成功后执行
uvicorn ...,FastAPI 启动。
注意:若迁移失败(例如数据库未就绪、连接串错误),整条 CMD 会失败,容器退出,backend 不会起来。可查看日志:docker compose logs backend。
一键迁移脚本(Docker):在项目根目录执行其一即可完成「起 db → 等就绪 → 执行 alembic upgrade head」:
- Windows PowerShell:
.\deploy\scripts\migrate.ps1 - Linux / macOS:
bash deploy/scripts/migrate.sh - 任意平台(Python):
python deploy/scripts/migrate.py(默认 Docker);本机迁移用python deploy/scripts/migrate.py --local
方式 B:本机执行迁移
适用于在本机用 Python 直接跑迁移、数据库可在本机访问(本地 PostgreSQL 或已映射端口的 Docker 数据库)的情况。
前提:
- 本机已安装 Python 3.12、PostgreSQL 客户端库(
psycopg2-binary)。 - 数据库已存在(例如 Docker 只起 db:
docker compose up -d db),且已知连接信息。
步骤:
-
进入 backend 目录(迁移必须在 backend 根目录执行,以便找到
alembic.ini和alembic/):cd backend -
准备环境变量(二选一):
- 在项目根目录已有
.env时,可在 backend 下复制一份,让app.config自动读取:cp ../.env .env - 或直接设置连接串(PowerShell 示例):
Linux/macOS:
$env:DATABASE_URL_SYNC = "postgresql://wecom:wecom_secret@localhost:5432/wecom_ai"若数据库在 Docker 且未改端口,主机填export DATABASE_URL_SYNC=postgresql://wecom:wecom_secret@localhost:5432/wecom_ailocalhost、端口5432即可。
- 在项目根目录已有
-
安装依赖(若未装过):
pip install -r requirements.txt -
执行迁移:
alembic upgrade head- Alembic 会读取当前目录下的
alembic.ini和alembic/env.py。 env.py里会from app.config import settings、from app.models import Base,因此当前目录必须在 backend(或设置PYTHONPATH指向 backend),这样app才能正确解析。- 执行后,数据库中会创建/更新表,并写入
alembic_version。
- Alembic 会读取当前目录下的
-
验证:连接数据库查看是否有
users、audit_logs及alembic_version表。
本机常见问题:
- 报错
No module named 'app':未在backend目录执行,或未设置PYTHONPATH。解决:cd backend再执行,或PYTHONPATH=backend alembic -c backend/alembic.ini upgrade head(在项目根目录时)。 - 连接被拒绝:检查
DATABASE_URL_SYNC的主机、端口、用户名、密码、数据库名是否与真实 PostgreSQL 一致(Docker 时主机为localhost,端口一般为5432)。
2. 如何创建管理员
使用 seed 脚本(从 项目根目录 执行,依赖已安装的 Python 和 .env):
# 确保 .env 存在,且 DATABASE_URL_SYNC 指向数据库(Docker 时用 localhost:5432)
pip install python-dotenv bcrypt psycopg2-binary sqlalchemy
python deploy/scripts/seed.py
可选环境变量(在 .env 或导出):
ADMIN_USERNAME:默认adminADMIN_PASSWORD:默认adminDATABASE_URL_SYNC:同步连接串,Docker 时一般为postgresql://wecom:wecom_secret@localhost:5432/wecom_ai
脚本会检查 users 表是否存在;若不存在会提示先执行迁移。若用户名已存在则跳过创建。
3. Auth API
-
POST /api/auth/login
- Body:
{"username":"admin","password":"admin"} - 成功:200,
{"access_token":"...","token_type":"bearer"} - 失败:401/403
- Body:
-
GET /api/auth/me
- Header:
Authorization: Bearer <access_token> - 成功:200,当前用户信息(id、username、role、is_active、created_at)
- 失败:401/403
- Header:
4. 管理后台
- Token 存储:使用 localStorage,key 为
"token"。实现见admin/lib/api.ts注释。生产环境可改为 httpOnly Cookie 由后端 Set-Cookie。 - 登录页 /login:表单提交后调用
POST /api/auth/login,成功则写入 localStorage 并跳转/dashboard。 - Dashboard /dashboard:需登录;进入时请求
GET /api/auth/me,失败则清除 token 并跳转/login;成功则展示当前用户信息与退出按钮。 - 根路径 /:有 token 则跳转
/dashboard,否则跳转/login。
5. 安全
- 密码:bcrypt(passlib + bcrypt 4.1.2)。
- JWT:有过期时间,由
JWT_EXPIRE_MINUTES控制(默认 60 分钟);密钥JWT_SECRET需在生产环境修改。
6. 如何登录验证
- 启动:
docker compose up -d(自动执行迁移)。 - 创建管理员:
python deploy/scripts/seed.py(见上文)。 - 打开 http://localhost 或 http://localhost:3000,应跳转登录页。
- 输入 admin / admin(或你在 .env 里设置的
ADMIN_USERNAME/ADMIN_PASSWORD),提交后应跳转 Dashboard 并显示当前用户信息。 - 点击退出后应回到登录页;再次访问 /dashboard 应被重定向到 /login。
直连 API 验证:
# 登录取 token
TOKEN=$(curl -s -X POST http://localhost:8000/api/auth/login -H "Content-Type: application/json" -d '{"username":"admin","password":"admin"}' | jq -r .access_token)
# 当前用户
curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8000/api/auth/me