Files
g0v0-server/app/router/private/admin.py

157 lines
4.6 KiB
Python

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