feat: add admin UI frontend and complete backend APIs
Some checks failed
continuous-integration/drone/push Build is failing
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)
This commit is contained in:
223
backend/app/routers/auth.py
Normal file
223
backend/app/routers/auth.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""认证路由"""
|
||||
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}
|
||||
Reference in New Issue
Block a user