from __future__ import annotations import json import sqlite3 from datetime import datetime, timezone from typing import Any class ClientsRepository: def __init__(self, conn: sqlite3.Connection) -> None: self._conn = conn @staticmethod def _now() -> str: return datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace( "+00:00", "Z", ) def create_client( self, client_id: str, name: str, token_hash: str | None = None, status: str = "ACTIVE", metadata: dict[str, Any] | None = None, ) -> None: metadata_json = ( json.dumps( metadata, ensure_ascii=False, separators=(",", ":"), sort_keys=True, ) if metadata is not None else None ) self._conn.execute( """ INSERT INTO crack_clients ( id, name, token_hash, status, created_at, last_seen_at, metadata_json ) VALUES (?, ?, ?, ?, ?, ?, ?) """, ( client_id, name, token_hash, status, self._now(), None, metadata_json, ), ) def get_client(self, client_id: str) -> sqlite3.Row | None: return self._conn.execute( """ SELECT id, name, token_hash, status, created_at, last_seen_at, metadata_json FROM crack_clients WHERE id = ? """, (client_id,), ).fetchone() def list_clients(self) -> list[sqlite3.Row]: return self._conn.execute( """ SELECT id, name, token_hash, status, created_at, last_seen_at, metadata_json FROM crack_clients ORDER BY id """ ).fetchall() def update_last_seen(self, client_id: str) -> None: self._conn.execute( """ UPDATE crack_clients SET last_seen_at = ? WHERE id = ? """, ( self._now(), client_id, ), ) def update_status(self, client_id: str, status: str) -> None: self._conn.execute( """ UPDATE crack_clients SET status = ? WHERE id = ? """, ( status, client_id, ), )