fix(api): support configurable users password column mapping

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-05-01 11:21:57 +08:00
parent c8ec27c6aa
commit 8c9699cd46
6 changed files with 34 additions and 1 deletions
+1
View File
@@ -11,6 +11,7 @@ DB_SCHEMA=public
DB_USERNAME=fquiz
DB_PASSWORD=fquiz
USER_USERNAME_COLUMN=username
USER_PASSWORD_COLUMN=password_hash
FILE_VFS_ROOT=./data/vfs
MINIO_ENABLED=true
MINIO_ENDPOINT=http://minio:9000
+1
View File
@@ -227,6 +227,7 @@
- API 启动初始化口径:`seed_defaults` 对本地目标执行;为兼容老表状态约束,初始管理员状态写入值统一为 `ENABLED`(不使用 `active`)。
- 用户表兼容口径:用户主键列对齐旧库 `users.user_id`,与用户关联的外键统一引用 `users.user_id`(不再引用 `users.id`)。
- 用户名列口径:历史环境存在 `users.username``users.user_name` 双形态;运行时通过 `USER_USERNAME_COLUMN``username`/`user_name`)与目标库对齐,避免启动阶段关系预加载触发 `UndefinedColumn`
- 密码列口径:历史环境存在 `users.password``users.password_hash` 双形态;运行时通过 `USER_PASSWORD_COLUMN``password`/`password_hash`)与目标库对齐,避免启动阶段关系预加载触发 `UndefinedColumn`
## 发布验收口径(2026-04-26
+1
View File
@@ -24,6 +24,7 @@ class Settings(BaseSettings):
db_username: str = "fquiz"
db_password: str = "fquiz"
user_username_column: Literal["username", "user_name"] = "username"
user_password_column: Literal["password", "password_hash"] = "password_hash"
file_vfs_root: str = "./data/vfs"
minio_enabled: bool = False
minio_endpoint: str = "http://minio:9000"
+4 -1
View File
@@ -35,7 +35,10 @@ class User(Base):
unique=True,
index=True,
)
password_hash: Mapped[str] = mapped_column("password", String(255))
password_hash: Mapped[str] = mapped_column(
settings.user_password_column,
String(255),
)
status: Mapped[str] = mapped_column("state", String(32), default="ENABLED", index=True)
created_at: Mapped[datetime] = mapped_column(
"create_date",
+3
View File
@@ -103,6 +103,7 @@ services:
DB_USERNAME: ${DB_USERNAME:-fquiz}
DB_PASSWORD: ${DB_PASSWORD:-fquiz}
USER_USERNAME_COLUMN: ${USER_USERNAME_COLUMN:-username}
USER_PASSWORD_COLUMN: ${USER_PASSWORD_COLUMN:-password_hash}
FILE_VFS_ROOT: ${FILE_VFS_ROOT:-./data/vfs}
MINIO_ENABLED: ${MINIO_ENABLED:-true}
MINIO_ENDPOINT: ${MINIO_ENDPOINT:-http://minio:9000}
@@ -177,6 +178,7 @@ services:
DB_USERNAME: ${DB_USERNAME:-fquiz}
DB_PASSWORD: ${DB_PASSWORD:-fquiz}
USER_USERNAME_COLUMN: ${USER_USERNAME_COLUMN:-username}
USER_PASSWORD_COLUMN: ${USER_PASSWORD_COLUMN:-password_hash}
CELERY_BROKER_URL: ${CELERY_BROKER_URL:-redis://redis:6379/0}
CELERY_RESULT_BACKEND: ${CELERY_RESULT_BACKEND:-redis://redis:6379/1}
CELERY_TIMEZONE: ${CELERY_TIMEZONE:-Asia/Shanghai}
@@ -214,6 +216,7 @@ services:
DB_USERNAME: ${DB_USERNAME:-fquiz}
DB_PASSWORD: ${DB_PASSWORD:-fquiz}
USER_USERNAME_COLUMN: ${USER_USERNAME_COLUMN:-username}
USER_PASSWORD_COLUMN: ${USER_PASSWORD_COLUMN:-password_hash}
CELERY_BROKER_URL: ${CELERY_BROKER_URL:-redis://redis:6379/0}
CELERY_RESULT_BACKEND: ${CELERY_RESULT_BACKEND:-redis://redis:6379/1}
CELERY_TIMEZONE: ${CELERY_TIMEZONE:-Asia/Shanghai}
+24
View File
@@ -106,3 +106,27 @@
- 风险与影响:
- 影响范围:仅 GitHub 发布脚本默认值,不影响已存在且手工维护的服务器 `.env`
- 若线上环境明确依赖 `5433`,需在服务器 `.env` 显式保留 `POSTGRES_PORT=5433`
## Work Log - 修复 API 启动时报 users.password 列不存在(2026-05-01
- 背景:
- 启动报错 `psycopg.errors.UndefinedColumn: column users.password does not exist`
- 触发点在 `seed_defaults -> _seed_permissions` 的查询过程中,SQLAlchemy 关系预加载 `Role.users` 时会查询 `users` 全字段;当前 ORM 将 `password_hash` 绑定到 `users.password`,但目标库字段为 `password_hash`
- 本次改动(最小闭环):
- 文件:`api/app/core/config.py`
- 新增配置 `user_password_column`(环境变量 `USER_PASSWORD_COLUMN`),可选值 `password` / `password_hash`,默认 `password_hash`
- 文件:`api/app/models/user.py`
- `User.password_hash` 改为基于 `settings.user_password_column` 动态映射,兼容两类历史库结构。
- 文件:`docker-compose.yml`
-`api` / `celery-worker` / `celery-beat` 注入 `USER_PASSWORD_COLUMN` 环境变量透传。
- 文件:`.env.example`
- 新增 `USER_PASSWORD_COLUMN=password_hash` 示例配置。
- 验证:
- `python3 -m py_compile api/app/core/config.py api/app/models/user.py api/app/services/seed_service.py` -> 通过。
- `rg -n "USER_PASSWORD_COLUMN|user_password_column" ...` 命中配置、模型、compose、env 示例,映射链路完整。
- 风险与影响:
- 影响面集中在 `User` 模型密码字段映射。
- 运行环境需确保 `USER_PASSWORD_COLUMN` 与目标数据库实际字段一致;若配置错误,启动阶段仍可能抛 `UndefinedColumn`