chore: 完成全量功能迭代与部署准备

- 移除冗余依赖包
- 新增账号禁用校验与用户管理能力
- 新增文档下载与管理页面文件展示
- 新增API文档页面与用户管理前端页面
- 重构时区处理与docker-compose部署配置
- 完善测试用例与项目文档
This commit is contained in:
2026-08-01 00:02:42 +08:00
parent f92eff6f65
commit 6c6f690788
19 changed files with 2000 additions and 485 deletions
+96
View File
@@ -17,6 +17,7 @@ import pytest
from app.core.users import (
UserExistsError,
UserNotFoundError,
UserStore,
UserStoreError,
bootstrap_admin,
@@ -225,6 +226,101 @@ class TestCountAdmins:
assert await store.count_admins() == 1
class TestEnabledField:
"""enabled 字段:默认 True、create 传参、get/list 回读、存量兼容"""
async def test_default_enabled_is_true(self):
record = await UserStore(FakeRedis()).create("alice", "password123")
assert record.enabled is True
async def test_create_with_enabled_false(self):
redis = FakeRedis()
store = UserStore(redis)
record = await store.create("alice", "password123", enabled=False)
assert record.enabled is False
# 持久化后回读仍为 False
assert (await store.get("alice")).enabled is False
# 落库 JSON 含 enabled 字段
assert json.loads(redis.store["user:alice"])["enabled"] is False
async def test_get_list_roundtrip_enabled(self):
store = UserStore(FakeRedis())
await store.create("alice", "password123", enabled=False)
await store.create("bob", "password123", role="admin")
assert (await store.get("alice")).enabled is False
assert (await store.get("bob")).enabled is True
records = {r.username: r for r in await store.list()}
assert records["alice"].enabled is False
assert records["bob"].enabled is True
async def test_legacy_record_without_enabled_defaults_true(self):
"""存量记录无 enabled 字段时按 True 兼容(get/list"""
redis = FakeRedis()
store = UserStore(redis)
# 直接写入无 enabled 字段的存量记录
redis.store["user:legacy"] = json.dumps(
{
"username": "legacy",
"role": "user",
"password_hash": "hash",
"salt": "00" * 16,
"must_change_password": False,
"created_at": "2024-01-01T00:00:00+00:00",
}
)
record = await store.get("legacy")
assert record is not None
assert record.enabled is True
records = await store.list()
assert records[0].enabled is True
class TestUpdateUser:
"""update_user:角色/启用状态更新与校验"""
async def test_update_role(self):
store = UserStore(FakeRedis())
await store.create("alice", "password123")
updated = await store.update_user("alice", role="admin")
assert updated.role == "admin"
assert updated.enabled is True # 未改动
# 持久化
record = await store.get("alice")
assert record is not None
assert record.role == "admin"
async def test_update_enabled(self):
store = UserStore(FakeRedis())
await store.create("alice", "password123", role="admin")
updated = await store.update_user("alice", enabled=False)
assert updated.enabled is False
assert updated.role == "admin" # 未改动
async def test_update_both(self):
store = UserStore(FakeRedis())
await store.create("alice", "password123")
updated = await store.update_user("alice", role="admin", enabled=False)
assert updated.role == "admin"
assert updated.enabled is False
async def test_user_not_found_raises(self):
with pytest.raises(UserNotFoundError):
await UserStore(FakeRedis()).update_user("nobody", role="admin")
async def test_invalid_role_raises_value_error(self):
store = UserStore(FakeRedis())
await store.create("alice", "password123")
with pytest.raises(ValueError):
await store.update_user("alice", role="superuser")
async def test_no_fields_noop(self):
store = UserStore(FakeRedis())
await store.create("alice", "password123", role="admin")
updated = await store.update_user("alice")
assert updated.role == "admin"
assert updated.enabled is True
class TestBootstrapAdmin:
"""空库引导创建默认管理员"""