Initial commit: 浼佷笟寰俊 AI 鏈哄櫒浜哄姪鐞?MVP
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
160
docs/stage2.md
Normal file
160
docs/stage2.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# 阶段 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`)。
|
||||
执行时:
|
||||
|
||||
1. 读取 `backend/alembic.ini` 和 `backend/alembic/env.py`。
|
||||
2. `env.py` 从 `app.config.settings` 读取 `database_url_sync`(即环境变量 `DATABASE_URL_SYNC`),用该同步连接串连接 PostgreSQL。
|
||||
3. 对比数据库中的 `alembic_version` 与本地迁移文件,将尚未执行的迁移按顺序执行(例如 `001_users_and_audit_logs.py` 会创建 `users`、`audit_logs` 表)。
|
||||
|
||||
---
|
||||
|
||||
### 方式 A:Docker 启动时自动迁移
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. 在项目根目录执行:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
2. Compose 会先启动 `db`,等待健康检查通过后再启动 `backend`。
|
||||
3. **backend 容器**的启动命令为:
|
||||
```text
|
||||
sh -c "alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000"
|
||||
```
|
||||
4. **迁移阶段**:
|
||||
- 容器内当前目录为 `/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`)。
|
||||
5. **应用启动**:迁移成功后执行 `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`),且已知连接信息。
|
||||
|
||||
**步骤:**
|
||||
|
||||
1. **进入 backend 目录**(迁移必须在 backend 根目录执行,以便找到 `alembic.ini` 和 `alembic/`):
|
||||
```bash
|
||||
cd backend
|
||||
```
|
||||
|
||||
2. **准备环境变量**(二选一):
|
||||
- 在项目根目录已有 `.env` 时,可在 backend 下复制一份,让 `app.config` 自动读取:
|
||||
```bash
|
||||
cp ../.env .env
|
||||
```
|
||||
- 或直接设置连接串(PowerShell 示例):
|
||||
```powershell
|
||||
$env:DATABASE_URL_SYNC = "postgresql://wecom:wecom_secret@localhost:5432/wecom_ai"
|
||||
```
|
||||
Linux/macOS:
|
||||
```bash
|
||||
export DATABASE_URL_SYNC=postgresql://wecom:wecom_secret@localhost:5432/wecom_ai
|
||||
```
|
||||
若数据库在 Docker 且未改端口,主机填 `localhost`、端口 `5432` 即可。
|
||||
|
||||
3. **安装依赖**(若未装过):
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
4. **执行迁移**:
|
||||
```bash
|
||||
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`。
|
||||
|
||||
5. **验证**:连接数据库查看是否有 `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):
|
||||
|
||||
```bash
|
||||
# 确保 .env 存在,且 DATABASE_URL_SYNC 指向数据库(Docker 时用 localhost:5432)
|
||||
pip install python-dotenv bcrypt psycopg2-binary sqlalchemy
|
||||
|
||||
python deploy/scripts/seed.py
|
||||
```
|
||||
|
||||
可选环境变量(在 .env 或导出):
|
||||
|
||||
- `ADMIN_USERNAME`:默认 `admin`
|
||||
- `ADMIN_PASSWORD`:默认 `admin`
|
||||
- `DATABASE_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
|
||||
|
||||
- **GET /api/auth/me**
|
||||
- Header: `Authorization: Bearer <access_token>`
|
||||
- 成功:200,当前用户信息(id、username、role、is_active、created_at)
|
||||
- 失败:401/403
|
||||
|
||||
## 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. 如何登录验证
|
||||
|
||||
1. 启动:`docker compose up -d`(自动执行迁移)。
|
||||
2. 创建管理员:`python deploy/scripts/seed.py`(见上文)。
|
||||
3. 打开 http://localhost 或 http://localhost:3000,应跳转登录页。
|
||||
4. 输入 admin / admin(或你在 .env 里设置的 `ADMIN_USERNAME` / `ADMIN_PASSWORD`),提交后应跳转 Dashboard 并显示当前用户信息。
|
||||
5. 点击退出后应回到登录页;再次访问 /dashboard 应被重定向到 /login。
|
||||
|
||||
直连 API 验证:
|
||||
|
||||
```bash
|
||||
# 登录取 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
|
||||
```
|
||||
Reference in New Issue
Block a user