Some checks failed
continuous-integration/drone/push Build is failing
- Add Vue 3 frontend with Element Plus - Implement login, dashboard, tenant management - Add app configuration, logs viewer, stats pages - Add user management for admins - Update Drone CI to build and deploy frontend - Frontend ports: 3001 (test), 4001 (prod)
224 lines
5.9 KiB
Python
224 lines
5.9 KiB
Python
"""认证路由"""
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..database import get_db
|
|
from ..services.auth import (
|
|
authenticate_user,
|
|
create_access_token,
|
|
decode_token,
|
|
update_last_login,
|
|
hash_password,
|
|
TokenData,
|
|
UserInfo
|
|
)
|
|
from ..models.user import User
|
|
|
|
router = APIRouter(prefix="/auth", tags=["认证"])
|
|
security = HTTPBearer()
|
|
|
|
|
|
class LoginRequest(BaseModel):
|
|
"""登录请求"""
|
|
username: str
|
|
password: str
|
|
|
|
|
|
class LoginResponse(BaseModel):
|
|
"""登录响应"""
|
|
success: bool
|
|
token: Optional[str] = None
|
|
user: Optional[UserInfo] = None
|
|
error: Optional[str] = None
|
|
|
|
|
|
class ChangePasswordRequest(BaseModel):
|
|
"""修改密码请求"""
|
|
old_password: str
|
|
new_password: str
|
|
|
|
|
|
# 权限依赖
|
|
|
|
async def get_current_user(
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
db: Session = Depends(get_db)
|
|
) -> User:
|
|
"""获取当前用户"""
|
|
token = credentials.credentials
|
|
token_data = decode_token(token)
|
|
|
|
if not token_data:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Token 无效或已过期"
|
|
)
|
|
|
|
user = db.query(User).filter(User.id == token_data.user_id).first()
|
|
if not user or user.status != 1:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="用户不存在或已禁用"
|
|
)
|
|
|
|
return user
|
|
|
|
|
|
async def require_admin(user: User = Depends(get_current_user)) -> User:
|
|
"""要求管理员权限"""
|
|
if user.role != 'admin':
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="需要管理员权限"
|
|
)
|
|
return user
|
|
|
|
|
|
async def require_operator(user: User = Depends(get_current_user)) -> User:
|
|
"""要求操作员以上权限"""
|
|
if user.role not in ('admin', 'operator'):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="需要操作员以上权限"
|
|
)
|
|
return user
|
|
|
|
|
|
# API 端点
|
|
|
|
@router.post("/login", response_model=LoginResponse)
|
|
async def login(request: LoginRequest, db: Session = Depends(get_db)):
|
|
"""用户登录"""
|
|
user = authenticate_user(db, request.username, request.password)
|
|
|
|
if not user:
|
|
return LoginResponse(success=False, error="用户名或密码错误")
|
|
|
|
# 更新登录时间
|
|
update_last_login(db, user.id)
|
|
|
|
# 生成 Token
|
|
token = create_access_token({
|
|
"user_id": user.id,
|
|
"username": user.username,
|
|
"role": user.role
|
|
})
|
|
|
|
return LoginResponse(
|
|
success=True,
|
|
token=token,
|
|
user=UserInfo(
|
|
id=user.id,
|
|
username=user.username,
|
|
nickname=user.nickname,
|
|
role=user.role
|
|
)
|
|
)
|
|
|
|
|
|
@router.get("/me", response_model=UserInfo)
|
|
async def get_me(user: User = Depends(get_current_user)):
|
|
"""获取当前用户信息"""
|
|
return UserInfo(
|
|
id=user.id,
|
|
username=user.username,
|
|
nickname=user.nickname,
|
|
role=user.role
|
|
)
|
|
|
|
|
|
@router.post("/change-password")
|
|
async def change_password(
|
|
request: ChangePasswordRequest,
|
|
user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""修改密码"""
|
|
from ..services.auth import verify_password
|
|
|
|
if not verify_password(request.old_password, user.password_hash):
|
|
raise HTTPException(status_code=400, detail="原密码错误")
|
|
|
|
new_hash = hash_password(request.new_password)
|
|
db.query(User).filter(User.id == user.id).update({"password_hash": new_hash})
|
|
db.commit()
|
|
|
|
return {"success": True, "message": "密码修改成功"}
|
|
|
|
|
|
@router.get("/users")
|
|
async def list_users(
|
|
user: User = Depends(require_admin),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""获取用户列表(仅管理员)"""
|
|
users = db.query(User).all()
|
|
return [
|
|
{
|
|
"id": u.id,
|
|
"username": u.username,
|
|
"nickname": u.nickname,
|
|
"role": u.role,
|
|
"status": u.status,
|
|
"last_login_at": u.last_login_at,
|
|
"created_at": u.created_at
|
|
}
|
|
for u in users
|
|
]
|
|
|
|
|
|
class CreateUserRequest(BaseModel):
|
|
username: str
|
|
password: str
|
|
nickname: Optional[str] = None
|
|
role: str = "viewer"
|
|
|
|
|
|
@router.post("/users")
|
|
async def create_user(
|
|
request: CreateUserRequest,
|
|
user: User = Depends(require_admin),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""创建用户(仅管理员)"""
|
|
# 检查用户名是否存在
|
|
exists = db.query(User).filter(User.username == request.username).first()
|
|
if exists:
|
|
raise HTTPException(status_code=400, detail="用户名已存在")
|
|
|
|
new_user = User(
|
|
username=request.username,
|
|
password_hash=hash_password(request.password),
|
|
nickname=request.nickname,
|
|
role=request.role,
|
|
status=1
|
|
)
|
|
db.add(new_user)
|
|
db.commit()
|
|
db.refresh(new_user)
|
|
|
|
return {"success": True, "id": new_user.id}
|
|
|
|
|
|
@router.delete("/users/{user_id}")
|
|
async def delete_user(
|
|
user_id: int,
|
|
user: User = Depends(require_admin),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""删除用户(仅管理员)"""
|
|
if user_id == user.id:
|
|
raise HTTPException(status_code=400, detail="不能删除自己")
|
|
|
|
target = db.query(User).filter(User.id == user_id).first()
|
|
if not target:
|
|
raise HTTPException(status_code=404, detail="用户不存在")
|
|
|
|
db.delete(target)
|
|
db.commit()
|
|
|
|
return {"success": True}
|