PingWatch 网络设备离线监控系统

- FastAPI 后端 + Vue 3 前端
- Docker Compose 一键部署
- Casdoor OAuth 认证集成
- LogHive 集中式日志
- 设备批量 CSV 导入/导出
- WebSocket 实时状态推送
- 企业微信告警通知
- fping 高性能并发 Ping 检测

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 15:02:04 +08:00
commit 848f804169
55 changed files with 5941 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
"""Casdoor 认证 API"""
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel
from app.core.deps import get_db
from app.core.auth import create_access_token, exchange_code_for_user, get_current_user
from app.models.user import User, UserRoleEnum
router = APIRouter(prefix="/api/auth", tags=["认证"])
class LoginRequest(BaseModel):
code: str # Casdoor 返回的 OAuth code
class LoginResponse(BaseModel):
token: str
user: dict
@router.post("/login", response_model=LoginResponse)
async def login(data: LoginRequest, db: AsyncSession = Depends(get_db)):
"""用 Casdoor OAuth code 登录"""
casdoor_user = await exchange_code_for_user(data.code)
if not casdoor_user:
raise HTTPException(status_code=401, detail="Casdoor 认证失败")
# 从 id_token 中提取用户信息
# id_token payload 一般包含: sub, name, preferred_username, email, displayName 等
casdoor_uid = casdoor_user.get("sub") or casdoor_user.get("name", "")
username = casdoor_user.get("preferred_username") or casdoor_user.get("name", casdoor_uid)
display_name = casdoor_user.get("displayName", "")
if not casdoor_uid:
# 尝试用 name 作为备用标识
casdoor_uid = casdoor_user.get("name", "")
if not casdoor_uid:
raise HTTPException(status_code=401, detail="无法从 Casdoor 获取用户标识")
# 查找或创建本地用户
result = await db.execute(select(User).where(User.casdoor_uid == casdoor_uid))
user = result.scalar_one_or_none()
if not user:
# 首次登录,自动创建 viewer 账号
user = User(
casdoor_uid=casdoor_uid,
username=username,
display_name=display_name or username,
role=UserRoleEnum.viewer,
)
db.add(user)
await db.commit()
await db.refresh(user)
logger.info(f"新用户自动创建: {username} (uid: {casdoor_uid})")
# 更新最后登录时间
user.last_login_at = datetime.now()
await db.commit()
# 签发 PingWatch JWT
token = create_access_token(
data={"sub": user.casdoor_uid, "role": user.role.value}
)
return LoginResponse(
token=token,
user={
"id": user.id,
"username": user.username,
"display_name": user.display_name,
"role": user.role.value,
},
)
@router.get("/me")
async def get_me(current_user: User = Depends(get_current_user)):
"""获取当前登录用户信息"""
return {
"id": current_user.id,
"username": current_user.username,
"display_name": current_user.display_name,
"role": current_user.role.value,
}