Initial commit - DesignerCEP Project with Caddy deployment
This commit is contained in:
99
Server/app/api/v1/auth.py
Normal file
99
Server/app/api/v1/auth.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from app.schemas.auth import (
|
||||
UserLogin, UserRegister, Token, UserLogout, UserHeartbeat,
|
||||
VerifyRequest, VerifyResponse, VerifyEmailRequest,
|
||||
ForgotPasswordRequest, ResetPasswordRequest, SendVerificationCodeRequest
|
||||
)
|
||||
from app.services.auth_service import auth_service
|
||||
from app.db import get_db
|
||||
from app.models.session import UserSession
|
||||
from app.models.user import User
|
||||
from app.core.security import get_current_user
|
||||
from datetime import datetime, timezone
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.post("/send-verification-code")
|
||||
async def send_verification_code(body: SendVerificationCodeRequest, db: Session = Depends(get_db)):
|
||||
# 发送注册验证码
|
||||
return auth_service.send_verification_code(db, body.email)
|
||||
|
||||
@router.post("/login", response_model=Token)
|
||||
async def login(login_data: UserLogin, db: Session = Depends(get_db)):
|
||||
# 登录接口:校验用户密码,返回访问令牌
|
||||
return auth_service.login(db, login_data)
|
||||
|
||||
@router.post("/register", response_model=Token)
|
||||
async def register(register_data: UserRegister, db: Session = Depends(get_db)):
|
||||
# 注册接口:创建新用户,返回访问令牌
|
||||
return auth_service.register(db, register_data)
|
||||
|
||||
@router.post("/verify-email")
|
||||
async def verify_email(body: VerifyEmailRequest, db: Session = Depends(get_db)):
|
||||
return auth_service.verify_email(db, body.username, body.code)
|
||||
|
||||
@router.post("/forgot-password")
|
||||
async def forgot_password(body: ForgotPasswordRequest, db: Session = Depends(get_db)):
|
||||
return auth_service.forgot_password(db, body.email)
|
||||
|
||||
@router.post("/reset-password")
|
||||
async def reset_password(body: ResetPasswordRequest, db: Session = Depends(get_db)):
|
||||
if body.new_password != body.confirm_password:
|
||||
raise HTTPException(status_code=400, detail="两次输入的密码不一致")
|
||||
# 传入 email 参数
|
||||
return auth_service.reset_password(db, body.token, body.new_password, body.email)
|
||||
|
||||
@router.post("/logout")
|
||||
async def logout(body: UserLogout, db: Session = Depends(get_db)):
|
||||
# 登出接口:将指定设备会话置为非活跃
|
||||
return auth_service.logout(db, body.username, body.device_id)
|
||||
|
||||
@router.get("/online-time/{username}")
|
||||
async def get_online_time(username: str, db: Session = Depends(get_db)):
|
||||
# 在线时长统计:累计历史会话的时长(秒),以及当前活跃会话的实时时长(秒)
|
||||
user = db.query(User).filter(User.username == username).first()
|
||||
if not user:
|
||||
return {"username": username, "total_seconds": 0, "active_seconds": 0}
|
||||
# 历史累计(已登出的会话)
|
||||
total = db.query(UserSession).filter(
|
||||
UserSession.user_id == user.id,
|
||||
UserSession.active == False,
|
||||
UserSession.duration_seconds != None
|
||||
).with_entities(UserSession.duration_seconds).all()
|
||||
total_seconds = sum([d[0] for d in total]) if total else 0
|
||||
# 当前活跃会话实时时长
|
||||
now = datetime.now(timezone.utc)
|
||||
active = db.query(UserSession).filter(
|
||||
UserSession.user_id == user.id,
|
||||
UserSession.active == True,
|
||||
UserSession.login_at != None
|
||||
).first()
|
||||
if active:
|
||||
login_at = active.login_at
|
||||
if login_at and login_at.tzinfo is None:
|
||||
login_at = login_at.replace(tzinfo=timezone.utc)
|
||||
|
||||
last_seen = active.last_seen_at or login_at
|
||||
if last_seen and last_seen.tzinfo is None:
|
||||
last_seen = last_seen.replace(tzinfo=timezone.utc)
|
||||
|
||||
# 判定是否在线:如果 last_seen 在最近 2 分钟内,则认为在线,用 now 计算实时时长
|
||||
# 否则认为已断开(异常退出),用 last_seen 计算截止时长
|
||||
# 阈值设为 120 秒(假设前端心跳间隔为 60 秒)
|
||||
is_online = (now - last_seen).total_seconds() < 120
|
||||
|
||||
if is_online:
|
||||
end_time = now
|
||||
else:
|
||||
end_time = last_seen
|
||||
|
||||
active_seconds = int(max(0, (end_time - login_at).total_seconds())) if login_at else 0
|
||||
else:
|
||||
active_seconds = 0
|
||||
return {"username": username, "total_seconds": total_seconds, "active_seconds": active_seconds}
|
||||
|
||||
@router.post("/heartbeat")
|
||||
async def heartbeat(body: UserHeartbeat, db: Session = Depends(get_db)):
|
||||
# 心跳接口:更新会话的最近在线时间
|
||||
return auth_service.heartbeat(db, body.username, body.device_id)
|
||||
Reference in New Issue
Block a user