Files

233 lines
6.0 KiB
Python

from fastapi import APIRouter, Depends, Query, Request
from pydantic import BaseModel
from sqlalchemy.orm import Session
from app.core.deps import require_staff
from app.db import get_db
from app.services import rbac as rbac_service
public_router = APIRouter()
router = APIRouter(dependencies=[Depends(require_staff)])
class LoginIn(BaseModel):
username: str
password: str
captchaId: str = ""
captchaCode: str = ""
class OwnPasswordIn(BaseModel):
oldPassword: str
newPassword: str
class UserCreateIn(BaseModel):
username: str
password: str
displayName: str
mobile: str | None = None
roleIds: list[int] = []
class UserUpdateIn(BaseModel):
displayName: str
mobile: str | None = None
status: str
roleIds: list[int] = []
class ResetPasswordIn(BaseModel):
password: str | None = None
newPassword: str | None = None
class RoleCreateIn(BaseModel):
code: str
name: str
description: str | None = None
class RoleUpdateIn(BaseModel):
name: str
description: str | None = None
status: str
class RoleResourcesIn(BaseModel):
resourceIds: list[int] = []
class ResourceIn(BaseModel):
parentId: int | None = None
type: str
name: str
title: str
path: str | None = ""
component: str | None = ""
icon: str | None = ""
permission: str | None = ""
sort: int = 0
visible: bool = True
status: str = "active"
def _actor_id(principal: dict = Depends(require_staff)) -> int:
try:
return int(principal["sub"])
except (TypeError, ValueError):
from fastapi import HTTPException
raise HTTPException(status_code=401, detail="登录已失效")
def _resource_payload(body: ResourceIn) -> dict:
return {
"parent_id": body.parentId,
"type": body.type,
"name": body.name,
"title": body.title,
"path": body.path,
"component": body.component,
"icon": body.icon,
"permission": body.permission,
"sort": body.sort,
"visible": body.visible,
"status": body.status,
}
@public_router.post("/auth/login")
def login(body: LoginIn, request: Request, db: Session = Depends(get_db)) -> dict:
request.state.audit_username = (body.username or "").strip()[:64]
return rbac_service.authenticate(db, body.username, body.password, body.captchaId, body.captchaCode)
@router.post("/auth/password")
def change_password(
body: OwnPasswordIn,
actor_id: int = Depends(_actor_id),
db: Session = Depends(get_db),
) -> dict:
return rbac_service.change_own_password(db, actor_id, body.oldPassword, body.newPassword)
@router.get("/users")
def list_users(
current: int = Query(1, ge=1),
size: int = Query(10, ge=1, le=200),
username: str | None = None,
displayName: str | None = None,
status: str | None = None,
db: Session = Depends(get_db),
) -> dict:
return rbac_service.query_users(
db,
current=current,
size=size,
username=username,
display_name=displayName,
status_value=status,
)
@router.post("/users")
def create_user(body: UserCreateIn, db: Session = Depends(get_db)) -> dict:
return rbac_service.create_user(
db,
username=body.username,
password=body.password,
display_name=body.displayName,
mobile=body.mobile,
role_ids=body.roleIds,
)
@router.put("/users/{user_id}")
def update_user(
user_id: int,
body: UserUpdateIn,
actor_id: int = Depends(_actor_id),
db: Session = Depends(get_db),
) -> dict:
return rbac_service.update_user(
db,
user_id,
actor_id,
display_name=body.displayName,
status_value=body.status,
role_ids=body.roleIds,
mobile=body.mobile,
mobile_provided="mobile" in body.model_fields_set,
)
@router.post("/users/{user_id}/password")
def reset_password(user_id: int, body: ResetPasswordIn, db: Session = Depends(get_db)) -> dict:
plain = body.password if body.password is not None else body.newPassword
if plain is None:
from fastapi import HTTPException
raise HTTPException(status_code=400, detail="请填写密码")
return rbac_service.reset_user_password(db, user_id, plain)
@router.get("/roles")
def list_roles(
current: int = Query(1, ge=1),
size: int = Query(10, ge=1, le=200),
name: str | None = None,
code: str | None = None,
status: str | None = None,
db: Session = Depends(get_db),
) -> dict:
return rbac_service.query_roles(
db,
current=current,
size=size,
name=name,
code=code,
status_value=status,
)
@router.post("/roles")
def create_role(body: RoleCreateIn, db: Session = Depends(get_db)) -> dict:
return rbac_service.create_role(db, code=body.code, name=body.name, description=body.description)
@router.put("/roles/{role_id}")
def update_role(role_id: int, body: RoleUpdateIn, db: Session = Depends(get_db)) -> dict:
return rbac_service.update_role(
db,
role_id,
name=body.name,
description=body.description,
status_value=body.status,
)
@router.put("/roles/{role_id}/resources")
def replace_role_resources(role_id: int, body: RoleResourcesIn, db: Session = Depends(get_db)) -> dict:
return rbac_service.replace_role_resources(db, role_id, body.resourceIds)
@router.get("/resources")
def list_resources(db: Session = Depends(get_db)) -> list[dict]:
return rbac_service.list_resources(db)
@router.post("/resources")
def create_resource(body: ResourceIn, db: Session = Depends(get_db)) -> dict:
return rbac_service.create_resource(db, _resource_payload(body))
@router.put("/resources/{resource_id}")
def update_resource(resource_id: int, body: ResourceIn, db: Session = Depends(get_db)) -> dict:
return rbac_service.update_resource(db, resource_id, _resource_payload(body))
@router.delete("/resources/{resource_id}")
def delete_resource(resource_id: int, db: Session = Depends(get_db)) -> dict:
return rbac_service.delete_resource(db, resource_id)