chore(home): mirror from kalinamall (9883e6a) with papatramp URLs
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"""Управление зарегистрированными устройствами Seaca."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.mobile_device import MobileDevice
|
||||
from app.models.user import User
|
||||
from app.services.mobile_tokens import revoke_refresh_tokens_for_device
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MobileDeviceSummary:
|
||||
id: int
|
||||
user_id: int
|
||||
username: str
|
||||
device_uuid: str
|
||||
display_name: str
|
||||
platform: str
|
||||
app_version: str | None
|
||||
has_fcm_token: bool
|
||||
enrolled_at: datetime
|
||||
last_seen_at: datetime | None
|
||||
revoked_at: datetime | None
|
||||
is_active: bool
|
||||
|
||||
|
||||
def list_mobile_devices(db: Session) -> list[MobileDeviceSummary]:
|
||||
rows = db.scalars(select(MobileDevice).order_by(MobileDevice.enrolled_at.desc())).all()
|
||||
user_ids = {r.user_id for r in rows}
|
||||
usernames: dict[int, str] = {}
|
||||
if user_ids:
|
||||
for user in db.scalars(select(User).where(User.id.in_(user_ids))).all():
|
||||
usernames[user.id] = user.username
|
||||
return [_to_summary(row, usernames.get(row.user_id, "?")) for row in rows]
|
||||
|
||||
|
||||
def _to_summary(row: MobileDevice, username: str) -> MobileDeviceSummary:
|
||||
return MobileDeviceSummary(
|
||||
id=row.id,
|
||||
user_id=row.user_id,
|
||||
username=username,
|
||||
device_uuid=row.device_uuid,
|
||||
display_name=row.display_name or "",
|
||||
platform=row.platform or "android",
|
||||
app_version=row.app_version,
|
||||
has_fcm_token=bool((row.fcm_token or "").strip()),
|
||||
enrolled_at=row.enrolled_at,
|
||||
last_seen_at=row.last_seen_at,
|
||||
revoked_at=row.revoked_at,
|
||||
is_active=row.revoked_at is None,
|
||||
)
|
||||
|
||||
|
||||
def get_device_or_raise(db: Session, device_id: int) -> MobileDevice:
|
||||
row = db.get(MobileDevice, device_id)
|
||||
if row is None:
|
||||
raise ValueError("device not found")
|
||||
return row
|
||||
|
||||
|
||||
def assert_device_active(device: MobileDevice) -> None:
|
||||
if device.revoked_at is not None:
|
||||
raise PermissionError("device has been revoked")
|
||||
|
||||
|
||||
def touch_device(db: Session, device: MobileDevice, *, commit: bool = False) -> None:
|
||||
device.last_seen_at = datetime.now(timezone.utc)
|
||||
if commit:
|
||||
db.commit()
|
||||
|
||||
|
||||
def revoke_device(db: Session, device_id: int) -> MobileDeviceSummary:
|
||||
device = get_device_or_raise(db, device_id)
|
||||
if device.revoked_at is None:
|
||||
device.revoked_at = datetime.now(timezone.utc)
|
||||
revoke_refresh_tokens_for_device(db, device.id)
|
||||
db.commit()
|
||||
db.refresh(device)
|
||||
user = db.get(User, device.user_id)
|
||||
username = user.username if user else "?"
|
||||
return _to_summary(device, username)
|
||||
|
||||
|
||||
def delete_device_record(db: Session, device_id: int) -> None:
|
||||
device = get_device_or_raise(db, device_id)
|
||||
revoke_refresh_tokens_for_device(db, device.id)
|
||||
db.delete(device)
|
||||
db.commit()
|
||||
|
||||
|
||||
def update_fcm_token(db: Session, device: MobileDevice, fcm_token: str) -> None:
|
||||
cleaned = (fcm_token or "").strip()
|
||||
if not cleaned:
|
||||
raise ValueError("fcm_token is required")
|
||||
now = datetime.now(timezone.utc)
|
||||
device.fcm_token = cleaned
|
||||
device.fcm_token_updated_at = now
|
||||
device.last_seen_at = now
|
||||
db.commit()
|
||||
Reference in New Issue
Block a user