233 lines
6.0 KiB
Python
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)
|