Files
languard-servers-manager/backend/core/servers/logfiles_router.py
Tran G. (Revernomad) Khoa bf09a6ed1c fix: fix Arma 3 log discovery and improve config editor UX
- Fix logfiles_router and thread_registry to resolve .rpt log files
  from Path(server["exe_path"]).parent/server/ instead of the languard
  data dir, which never contained log files — log list and live tail
  both now work correctly
- Rewrite get_ui_schema() in config_generator to cover all ~80 fields
  across all 5 sections (server/basic/profile/launch/rcon) with proper
  toggle/select/number/password/tag-list/hidden widgets and labels;
  missions field is hidden (managed by Missions tab)
- Add formatSelectDisplay() to ConfigEditor so select fields show
  descriptive text (e.g. "0 - Never") instead of raw numbers in view mode
- Add ToggleDisplay for boolean fields (Enabled/Disabled with indicator dot)
- Add section tab labels and descriptions to ConfigEditor
- Add MissionList UX hints and dynamic Add/In Rotation button labels
- Add "hidden" to FieldSchema widget union type
- Update API.md, ARCHITECTURE.md, CLAUDE.md, FRONTEND.md, MODULES.md,
  THREADING.md to document log path fix and schema coverage
2026-04-18 15:56:04 +07:00

83 lines
2.7 KiB
Python

"""Log file endpoints — list, download, and delete historical RPT log files."""
from __future__ import annotations
import logging
from pathlib import Path
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import FileResponse
from sqlalchemy.engine import Connection
from adapters.registry import GameAdapterRegistry
from core.dal.server_repository import ServerRepository
from database import get_db
from dependencies import get_current_user, require_admin
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/servers/{server_id}/logfiles", tags=["logfiles"])
def _ok(data):
return {"success": True, "data": data, "error": None}
def _get_rpt_parser(server_id: int, db: Connection):
server = ServerRepository(db).get_by_id(server_id)
if server is None:
raise HTTPException(status_code=404, detail="Server not found")
adapter = GameAdapterRegistry.get(server["game_type"])
if not adapter.has_capability("log_parser"):
raise HTTPException(status_code=404, detail="Server does not support log files")
# RPT files live next to the server exe (e.g. A3Master/server/*.rpt)
exe_dir = Path(server["exe_path"]).parent
return adapter.get_log_parser(), exe_dir
@router.get("")
def list_log_files(
server_id: int,
db: Annotated[Connection, Depends(get_db)],
_user: Annotated[dict, Depends(get_current_user)],
) -> dict:
parser, server_dir = _get_rpt_parser(server_id, db)
files = parser.list_log_files(server_dir)
return _ok(files)
@router.get("/{filename}/download")
def download_log_file(
server_id: int,
filename: str,
db: Annotated[Connection, Depends(get_db)],
_user: Annotated[dict, Depends(get_current_user)],
):
parser, server_dir = _get_rpt_parser(server_id, db)
path = parser.get_log_file_path(server_dir, filename)
if path is None:
raise HTTPException(status_code=404, detail="Log file not found")
return FileResponse(
path=str(path),
filename=filename,
media_type="text/plain",
)
@router.delete("/{filename}")
def delete_log_file(
server_id: int,
filename: str,
db: Annotated[Connection, Depends(get_db)],
_admin: Annotated[dict, Depends(require_admin)],
) -> dict:
parser, server_dir = _get_rpt_parser(server_id, db)
path = parser.get_log_file_path(server_dir, filename)
if path is None:
raise HTTPException(status_code=404, detail="Log file not found")
try:
path.unlink()
except OSError as exc:
raise HTTPException(status_code=500, detail=f"Could not delete file: {exc}") from exc
return _ok({"message": f"{filename} deleted"})