Files
languard-servers-manager/backend/core/dal/player_repository.py
Tran G. (Revernomad) Khoa 5a62d21def feat: implement phases 3-5 of Arma 3 UX enhancement plan
Phase 3 - Mod display names + split-pane selector:
- Parse mod.cpp/meta.cpp for display_name and workshop_id
- Rewrite ModList as two-pane available/selected interface

Phase 4 - Player kick/ban from Players tab:
- Add get_by_slot() to PlayerRepository
- Add get_rcon_client() class method to ThreadRegistry
- Add /players/{slot_id}/kick and /ban endpoints
- Rewrite PlayerTable with kick/ban modals and ban presets

Phase 5 - Historical log file browser:
- Add list_log_files() and get_log_file_path() to RPTParser
- Add logfiles_router with GET/download/DELETE endpoints
- Update LogViewer with collapsible log files section (download + delete)
2026-04-17 20:47:37 +07:00

76 lines
2.7 KiB
Python

import json
from datetime import datetime, timezone
from core.dal.base_repository import BaseRepository
class PlayerRepository(BaseRepository):
def get_all(self, server_id: int) -> list[dict]:
return self._fetchall(
"SELECT * FROM players WHERE server_id = :sid ORDER BY slot_id",
{"sid": server_id},
)
def count(self, server_id: int) -> int:
row = self._fetchone(
"SELECT COUNT(*) as cnt FROM players WHERE server_id = :sid",
{"sid": server_id},
)
return row["cnt"] if row else 0
def upsert(self, server_id: int, player: dict) -> None:
now = datetime.now(timezone.utc).isoformat()
self._execute(
"""
INSERT INTO players (server_id, slot_id, name, guid, ip, ping, game_data, joined_at, updated_at)
VALUES (:sid, :slot, :name, :guid, :ip, :ping, :gd, :now, :now)
ON CONFLICT(server_id, slot_id) DO UPDATE SET
name = excluded.name,
guid = excluded.guid,
ping = excluded.ping,
game_data = excluded.game_data,
updated_at = excluded.updated_at
""",
{
"sid": server_id,
"slot": str(player.get("slot_id", "")),
"name": player.get("name", ""),
"guid": player.get("guid"),
"ip": player.get("ip"),
"ping": player.get("ping"),
"gd": json.dumps(player.get("game_data", {})),
"now": now,
},
)
def get_by_slot(self, server_id: int, slot_id: int) -> dict | None:
return self._fetchone(
"SELECT * FROM players WHERE server_id = :sid AND slot_id = :slot",
{"sid": server_id, "slot": str(slot_id)},
)
def clear(self, server_id: int) -> None:
self._execute("DELETE FROM players WHERE server_id = :sid", {"sid": server_id})
def get_history(
self,
server_id: int,
limit: int = 50,
offset: int = 0,
search: str | None = None,
) -> tuple[int, list[dict]]:
conditions = ["server_id = :sid"]
params: dict = {"sid": server_id, "limit": limit, "offset": offset}
if search:
conditions.append("name LIKE :search")
params["search"] = f"%{search}%"
where = " AND ".join(conditions)
total_row = self._fetchone(
f"SELECT COUNT(*) as cnt FROM player_history WHERE {where}", params
)
total = total_row["cnt"] if total_row else 0
rows = self._fetchall(
f"SELECT * FROM player_history WHERE {where} ORDER BY left_at DESC LIMIT :limit OFFSET :offset",
params,
)
return total, rows