Files
g0v0-server/app/models/model.py
MingxuanGame 40670c094b feat(auth): support trusted device (#52)
New API to maintain sessions and devices:

- GET /api/private/admin/sessions
- DELETE /api/private/admin/sessions/{session_id}
- GET /api/private/admin/trusted-devices
- DELETE /api/private/admin/trusted-devices/{device_id}

Auth:

web clients request `/oauth/token` and `/api/v2/session/verify` with `X-UUID` header to save the client as trusted device.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-03 11:26:43 +08:00

87 lines
2.3 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from datetime import UTC, datetime
from app.models.score import GameMode
from pydantic import BaseModel, field_serializer
class UTCBaseModel(BaseModel):
@field_serializer("*", when_used="json")
def serialize_datetime(self, v, _info):
if isinstance(v, datetime):
if v.tzinfo is None:
v = v.replace(tzinfo=UTC)
return v.astimezone(UTC).isoformat().replace("+00:00", "Z")
return v
Cursor = dict[str, int | float]
class RespWithCursor(BaseModel):
cursor: Cursor | None = None
class PinAttributes(BaseModel):
is_pinned: bool
score_id: int
class CurrentUserAttributes(BaseModel):
can_beatmap_update_owner: bool | None = None
can_delete: bool | None = None
can_edit_metadata: bool | None = None
can_edit_tags: bool | None = None
can_hype: bool | None = None
can_hype_reason: str | None = None
can_love: bool | None = None
can_remove_from_loved: bool | None = None
is_watching: bool | None = None
new_hype_time: datetime | None = None
nomination_modes: list[GameMode] | None = None
remaining_hype: int | None = None
can_destroy: bool | None = None
can_reopen: bool | None = None
can_moderate_kudosu: bool | None = None
can_resolve: bool | None = None
vote_score: int | None = None
can_message: bool | None = None
can_message_error: str | None = None
last_read_id: int | None = None
can_new_comment: bool | None = None
can_new_comment_reason: str | None = None
pin: PinAttributes | None = None
@dataclass
class UserAgentInfo:
raw_ua: str = ""
browser: str | None = None
version: str | None = None
os: str | None = None
platform: str | None = None
is_mobile: bool = False
is_tablet: bool = False
is_pc: bool = False
is_client: bool = False
@property
def displayed_name(self) -> str:
parts = []
if self.browser:
parts.append(self.browser)
if self.version:
parts.append(self.version)
if self.os:
if parts:
parts.append(f"on {self.os}")
else:
parts.append(self.os)
return " ".join(parts) if parts else "Unknown"
def __str__(self) -> str:
return self.displayed_name