24335a2677
* feat(gateway): hashed key store with grant + hot reload Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(gateway): channel registry + aiohttp app (keyed auth, whoami, per-channel ws/proxy) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(gateway): manage_keys CLI (add/list/revoke) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(gateway): retire srebot_external, run relay-gateway under PM2 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(gateway): point ecosystem + README at relay-gateway Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tss): replay outbox producer for relay gateway Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tss): forward processed games to relay outbox Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tss-api): db helpers, app skeleton, info endpoint, fixtures Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tss-api): player, games, history, search endpoints Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tss-api): live, match, scoreboard, matches-search, maps Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tss-api): filter-required leaderboards (players/vehicles/stats) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(tss-api): tournament list/detail/standings/matches Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: wire tss upstream through gateway + tssbot-api PM2 app Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
1.3 KiB
Python
48 lines
1.3 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import sqlite3
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import aiosqlite
|
|
|
|
# Make TSSBOT root importable for BOT.storage defaults.
|
|
_TSSBOT_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(_TSSBOT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(_TSSBOT_ROOT))
|
|
|
|
|
|
def battles_path() -> Path:
|
|
override = os.getenv("TSS_API_BATTLES_DB", "").strip()
|
|
if override:
|
|
return Path(override)
|
|
from BOT.storage import TSS_BATTLES_DB_PATH
|
|
return TSS_BATTLES_DB_PATH
|
|
|
|
|
|
def tournaments_path() -> Path:
|
|
override = os.getenv("TSS_API_TOURNAMENTS_DB", "").strip()
|
|
if override:
|
|
return Path(override)
|
|
from BOT.storage import STORAGE_DIR
|
|
return STORAGE_DIR / "tss_tournaments.db"
|
|
|
|
|
|
def _ro_uri(path: Path) -> str:
|
|
return f"file:{path}?mode=ro"
|
|
|
|
|
|
async def query(path: Path, sql: str, params: tuple[Any, ...] = ()) -> list[dict]:
|
|
async with aiosqlite.connect(_ro_uri(path), uri=True) as conn:
|
|
conn.row_factory = sqlite3.Row
|
|
async with conn.execute(sql, params) as cur:
|
|
rows = await cur.fetchall()
|
|
return [dict(r) for r in rows]
|
|
|
|
|
|
async def query_one(path: Path, sql: str, params: tuple[Any, ...] = ()) -> dict | None:
|
|
rows = await query(path, sql, params)
|
|
return rows[0] if rows else None
|