6241543119
一、RBAC 权限管理(角色 → 可见页面) - 新增 admin_role 表 + 权限目录 permissions.py:角色持有一组页面 key,登录后左侧只展示这些页 - 内建角色 管理员(super_admin 全权锁定)/运营/财务/技术,页集对齐原型;key 承重(require_role 用),另加中文 label 展示 - 角色 CRUD 端点(仅 super_admin)+ 目录端点;登录/me 下发当前角色有效可见页 - admins 加删除、角色存在性校验;可复看已确定登录密码:UI 建的账号留存明文 plain_password,脚本建的超管不留存 二、系统配置下发修复 - 首页数据「保存即生效」:显式保存(apply_now)对 real/manual 直接落配置目标值、绕过只增不减护栏(修「改了 app 端不变」),护栏仍管自动 tick - 首页轮播新增数据源三选一:mixed(真实优先+种子)/real(只真实)/seed(只种子),get_feed 分支 + /marquee-seeds/mode 端点 - 福利页 Tab 隐藏 任务/里程碑,及看广告的单次金币/每轮次数/信息流开关:CONFIG_DEFS 加 hidden + list_config 过滤,业务读取默认值不受影响 三、比价记录店/商品搜索 - comparison_record 加 product_names 派生列(从下单 items 拼商品名),迁移建列并回填历史行 - admin 比价记录列表店/商品分列可搜:走 product_names 普通列 LIKE,规避 SQLite JSON 中文 ensure_ascii 转义搜不到的坑 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: zzhyyyyy <2685922758@qq.com> Reviewed-on: #117 Co-authored-by: zhuzihao <zhuzihao@wonderable.ai> Co-committed-by: zhuzihao <zhuzihao@wonderable.ai>
57 lines
2.3 KiB
Python
57 lines
2.3 KiB
Python
"""Admin 认证:账号密码登录 → admin JWT(独立 secret)。"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
from app.admin.deps import AdminDb, CurrentAdmin
|
|
from app.admin.repositories import admin_role as role_repo
|
|
from app.admin.repositories import admin_user as admin_repo
|
|
from app.admin.schemas.auth import AdminLoginRequest, AdminLoginResponse, AdminOut
|
|
from app.admin.security import create_admin_token
|
|
from app.core.ratelimit import rate_limit
|
|
from app.core.security import verify_password
|
|
|
|
logger = logging.getLogger("shagua.admin.auth")
|
|
|
|
router = APIRouter(prefix="/admin/api/auth", tags=["admin-auth"])
|
|
|
|
|
|
def _admin_out_with_pages(admin, db: AdminDb) -> AdminOut: # noqa: ANN001
|
|
"""AdminOut + 当前角色有效可见页(前端左侧导航按此过滤)。"""
|
|
out = AdminOut.model_validate(admin)
|
|
out.pages = role_repo.effective_pages_of(db, admin.role)
|
|
return out
|
|
|
|
|
|
@router.post(
|
|
"/login",
|
|
response_model=AdminLoginResponse,
|
|
summary="管理员登录",
|
|
dependencies=[Depends(rate_limit(10, 60, "admin-login"))], # 同 IP 每分钟≤10 次,防爆破
|
|
)
|
|
def login(req: AdminLoginRequest, db: AdminDb) -> AdminLoginResponse:
|
|
admin = admin_repo.get_by_username(db, req.username)
|
|
# 用户名不存在 / 密码错统一 401 同文案(防账号枚举)。
|
|
# disabled 账号单独 403"账号已禁用":admin 是内部少数已知账号、无枚举价值,
|
|
# 明确提示比防枚举更有运维价值(与 App 端 auth.py 对 disabled 用户的 403 一致)。
|
|
if admin is None or not verify_password(req.password, admin.password_hash):
|
|
raise HTTPException(status_code=401, detail="用户名或密码错误")
|
|
if admin.status != "active":
|
|
raise HTTPException(status_code=403, detail="账号已禁用")
|
|
|
|
token, expires_in = create_admin_token(admin_id=admin.id, role=admin.role)
|
|
admin_repo.update_last_login(db, admin)
|
|
logger.info("admin login ok id=%d username=%s role=%s", admin.id, admin.username, admin.role)
|
|
return AdminLoginResponse(
|
|
access_token=token,
|
|
expires_in=expires_in,
|
|
admin=_admin_out_with_pages(admin, db),
|
|
)
|
|
|
|
|
|
@router.get("/me", response_model=AdminOut, summary="当前管理员(含有效可见页)")
|
|
def me(admin: CurrentAdmin, db: AdminDb) -> AdminOut:
|
|
return _admin_out_with_pages(admin, db)
|