Add V2 admin console

This commit is contained in:
2026-07-06 18:13:08 +08:00
parent 301661e7b4
commit 62eeb3186c
34 changed files with 3132 additions and 1 deletions

View File

@@ -0,0 +1,83 @@
from __future__ import annotations
from datetime import UTC, datetime
from fastapi import HTTPException, status
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.core.config import get_settings
from app.core.security import create_access_token, hash_password, verify_password
from app.models.admin import Admin
from app.models.chat import ChatMessage, ChatSession
from app.models.knowledge import Knowledge
from app.models.logs import AiRequestLog, OperationLog
from app.models.user import User
class AdminAuthService:
@classmethod
def login(cls, db: Session, username: str, password: str) -> dict:
cls.ensure_bootstrap_admin(db)
admin = db.scalar(select(Admin).where(Admin.username == username))
if admin is None or not verify_password(password, admin.password):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="管理员账号或密码错误")
if admin.status != 1:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="管理员已禁用")
admin.last_login_at = datetime.now(UTC).replace(tzinfo=None)
db.add(admin)
db.commit()
db.refresh(admin)
token, expired_at = create_access_token(str(admin.id), "admin")
return {"token": token, "expiredAt": expired_at, "admin": admin}
@staticmethod
def ensure_bootstrap_admin(db: Session) -> None:
has_admin = db.scalar(select(func.count(Admin.id))) or 0
if has_admin:
return
settings = get_settings()
admin = Admin(
username=settings.bootstrap_admin_username,
password=hash_password(settings.bootstrap_admin_password),
name=settings.bootstrap_admin_name,
status=1,
)
db.add(admin)
db.commit()
class AdminDashboardService:
@staticmethod
def stats(db: Session) -> dict:
return {
"userCount": db.scalar(select(func.count(User.id)).where(User.is_deleted == 0)) or 0,
"sessionCount": db.scalar(select(func.count(ChatSession.id)).where(ChatSession.is_deleted == 0)) or 0,
"messageCount": db.scalar(select(func.count(ChatMessage.id))) or 0,
"aiRequestCount": db.scalar(select(func.count(AiRequestLog.id))) or 0,
"knowledgeCount": db.scalar(select(func.count(Knowledge.id))) or 0,
}
class OperationLogService:
@staticmethod
def write(
db: Session,
*,
admin_id: int | None,
module: str,
action: str,
target_id: int | None = None,
result: str = "SUCCESS",
) -> None:
db.add(
OperationLog(
admin_id=admin_id,
module=module,
action=action,
target_id=target_id,
result=result,
)
)