157 lines
4.6 KiB
Python
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
|