modify: 统一插件风格
This commit is contained in:
@@ -159,6 +159,10 @@ JWT Token 有效期 3 小时,载荷包含:`sub`(用户ID)、`username`
|
||||
|
||||
## 默认管理员账户
|
||||
|
||||
插件基于 `Plugin` 基类统一生命周期:`on_init` 中执行管理员种子逻辑
|
||||
(先按用户名、再按邮箱兜底查询;多进程并发启动时靠唯一约束 + `IntegrityError` 回滚保证幂等),
|
||||
数据库 `auth.db` 由基类统一初始化并在 cleanup 时释放。
|
||||
|
||||
首次启动时自动创建:
|
||||
|
||||
| 字段 | 值 |
|
||||
|
||||
+58
-49
@@ -1,65 +1,74 @@
|
||||
from pathlib import Path
|
||||
"""
|
||||
Auth Worker 插件 — 鉴权管理(登录 / 注册 / 资料 / 改密)
|
||||
|
||||
基于 Plugin 基类统一生命周期:数据库由基类初始化(auth.db),
|
||||
on_init 中执行 admin 幂等种子逻辑,on_cleanup 无需重复 dispose。
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from chongming_worker import auto_bind_models
|
||||
from chongming_worker.runtime import create_workers as _create_workers
|
||||
from chongming_worker.plugin import Plugin
|
||||
from chongming_password import hash_password
|
||||
|
||||
from .router import *
|
||||
from .router import * # noqa: F401,F403 触发 @rpc_method 注册
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
auto_bind_models("auth")
|
||||
class AuthPlugin(Plugin):
|
||||
"""Auth 插件 — 统一生命周期。"""
|
||||
|
||||
type = "auth"
|
||||
db_name = "auth.db" # 自动在 data_dir 下建 SQLite 库
|
||||
auto_bind = True # 首次初始化时自动 auto_bind_models("auth")
|
||||
|
||||
async def create_workers(session, config, count=1, worker_manager=None):
|
||||
from chongming_database import Database
|
||||
from .global_var import set_global_var
|
||||
from .models.auth import Users
|
||||
from sqlalchemy import select
|
||||
async def on_init(self):
|
||||
"""首次 create_workers 时调用一次:初始化 admin 账号(幂等)。"""
|
||||
from .models.auth import Users
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
db_path = Path(config.get("data_dir", "./data")) / "auth.db"
|
||||
db = Database(db_path)
|
||||
await db.initialize()
|
||||
db = self.db
|
||||
|
||||
# 初始化管理员(多进程并发启动时必须幂等,避免 UNIQUE 约束冲突)
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
async with db.get_session() as sess:
|
||||
select_stmt = select(Users) \
|
||||
.where(Users.username == "admin") # type: ignore
|
||||
result = await sess.execute(select_stmt)
|
||||
user: Users | None = result.scalar_one_or_none()
|
||||
|
||||
# 按用户名未命中时,再按邮箱兜底,避免历史数据中已存在同邮箱用户
|
||||
if user is None:
|
||||
# 初始化管理员(多进程并发启动时必须幂等,避免 UNIQUE 约束冲突)
|
||||
async with db.get_session() as sess:
|
||||
select_stmt = select(Users) \
|
||||
.where(Users.email == "admin@example.com") # type: ignore
|
||||
.where(Users.username == "admin") # type: ignore
|
||||
result = await sess.execute(select_stmt)
|
||||
user = result.scalar_one_or_none()
|
||||
user: Users | None = result.scalar_one_or_none()
|
||||
|
||||
if user is None:
|
||||
user = Users(
|
||||
username="admin",
|
||||
hashed_password=hash_password("admin"),
|
||||
email="admin@example.com",
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now(),
|
||||
is_superuser=True
|
||||
)
|
||||
sess.add(user)
|
||||
try:
|
||||
await sess.commit()
|
||||
except IntegrityError:
|
||||
# 其他并发进程已抢先创建 admin,当前进程忽略即可
|
||||
await sess.rollback()
|
||||
|
||||
set_global_var("db", db)
|
||||
# 按用户名未命中时,再按邮箱兜底,避免历史数据中已存在同邮箱用户
|
||||
if user is None:
|
||||
select_stmt = select(Users) \
|
||||
.where(Users.email == "admin@example.com") # type: ignore
|
||||
result = await sess.execute(select_stmt)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
return _create_workers(session, config, count, worker_manager)
|
||||
if user is None:
|
||||
user = Users(
|
||||
username="admin",
|
||||
hashed_password=hash_password("admin"),
|
||||
email="admin@example.com",
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now(),
|
||||
is_superuser=True
|
||||
)
|
||||
sess.add(user)
|
||||
try:
|
||||
await sess.commit()
|
||||
except IntegrityError:
|
||||
# 其他并发进程已抢先创建 admin,当前进程忽略即可
|
||||
await sess.rollback()
|
||||
|
||||
async def cleanup(worker_item: str):
|
||||
from .global_var import db
|
||||
# 写入全局变量供 router 使用(数据库由基类在 cleanup 时统一 dispose)
|
||||
self.set_var("db", db)
|
||||
|
||||
if db is not None:
|
||||
await db.dispose()
|
||||
print(f"{worker_item} 清理完毕")
|
||||
async def on_cleanup(self):
|
||||
"""cleanup 时调用(数据库由基类处理)。"""
|
||||
logger.info("[auth] 清理完成")
|
||||
|
||||
|
||||
# 暴露给 app lifespan 的模块级入口(bound method,签名与旧写法完全一致)
|
||||
plugin = AuthPlugin()
|
||||
create_workers = plugin.create_workers
|
||||
cleanup = plugin.cleanup
|
||||
|
||||
Reference in New Issue
Block a user