from __future__ import annotations from typing import Annotated from app.database.auth import OAuthToken from app.database.verification import LoginSession, LoginSessionResp, TrustedDevice, TrustedDeviceResp from app.dependencies.database import Database from app.dependencies.geoip import GeoIPService from app.dependencies.user import UserAndToken, get_client_user_and_token from .router import router from fastapi import HTTPException, Security from pydantic import BaseModel from sqlmodel import col, select class SessionsResp(BaseModel): total: int current: int = 0 sessions: list[LoginSessionResp] @router.get( "/admin/sessions", name="获取当前用户的登录会话列表", tags=["用户会话", "g0v0 API", "管理"], response_model=SessionsResp, ) async def get_sessions( session: Database, user_and_token: Annotated[UserAndToken, Security(get_client_user_and_token)], geoip: GeoIPService, ): current_user, token = user_and_token sessions = ( await session.exec( select( LoginSession, ) .where(LoginSession.user_id == current_user.id, col(LoginSession.is_verified).is_(True)) .order_by(col(LoginSession.created_at).desc()) ) ).all() return SessionsResp( total=len(sessions), current=token.id, sessions=[LoginSessionResp.from_db(s, geoip) for s in sessions], ) @router.delete( "/admin/sessions/{session_id}", name="注销指定的登录会话", tags=["用户会话", "g0v0 API", "管理"], status_code=204, ) async def delete_session( session: Database, session_id: int, user_and_token: Annotated[UserAndToken, Security(get_client_user_and_token)], ): current_user, token = user_and_token if session_id == token.id: raise HTTPException(status_code=400, detail="Cannot delete the current session") db_session = await session.get(LoginSession, session_id) if not db_session or db_session.user_id != current_user.id: raise HTTPException(status_code=404, detail="Session not found") await session.delete(db_session) token = await session.get(OAuthToken, db_session.token_id or 0) if token: await session.delete(token) await session.commit() return class TrustedDevicesResp(BaseModel): total: int current: int = 0 devices: list[TrustedDeviceResp] @router.get( "/admin/trusted-devices", name="获取当前用户的受信任设备列表", tags=["用户会话", "g0v0 API", "管理"], response_model=TrustedDevicesResp, ) async def get_trusted_devices( session: Database, user_and_token: Annotated[UserAndToken, Security(get_client_user_and_token)], geoip: GeoIPService, ): current_user, token = user_and_token devices = ( await session.exec( select(TrustedDevice) .where(TrustedDevice.user_id == current_user.id) .order_by(col(TrustedDevice.last_used_at).desc()) ) ).all() current_device_id = ( await session.exec( select(TrustedDevice.id) .join(LoginSession, col(LoginSession.device_id) == TrustedDevice.id) .where( LoginSession.token_id == token.id, TrustedDevice.user_id == current_user.id, ) .limit(1) ) ).first() return TrustedDevicesResp( total=len(devices), current=current_device_id or 0, devices=[TrustedDeviceResp.from_db(device, geoip) for device in devices], ) @router.delete( "/admin/trusted-devices/{device_id}", name="移除受信任设备", tags=["用户会话", "g0v0 API", "管理"], status_code=204, ) async def delete_trusted_device( session: Database, device_id: int, user_and_token: Annotated[UserAndToken, Security(get_client_user_and_token)], ): current_user, token = user_and_token device = await session.get(TrustedDevice, device_id) current_device_id = ( await session.exec( select(TrustedDevice.id) .join(LoginSession, col(LoginSession.device_id) == TrustedDevice.id) .where( LoginSession.token_id == token.id, TrustedDevice.user_id == current_user.id, ) .limit(1) ) ).first() if device_id == current_device_id: raise HTTPException(status_code=400, detail="Cannot delete the current trusted device") if not device or device.user_id != current_user.id: raise HTTPException(status_code=404, detail="Trusted device not found") await session.delete(device) await session.commit() return