Record worker browser sessions
This commit is contained in:
@@ -3,10 +3,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import secrets
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Any
|
||||
|
||||
import speedtest # type: ignore[import-untyped]
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -232,6 +235,63 @@ async def list_batch_runs(_: Auth) -> list[dict[str, object]]:
|
||||
return await batch_runs_repo.list() # type: ignore[return-value]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Browser-session recordings
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _recording_path(filename: str) -> Path:
|
||||
if filename != Path(filename).name or Path(filename).suffix.lower() != ".mp4":
|
||||
raise HTTPException(status_code=404, detail="Recording not found")
|
||||
directory = Path(config.recordings_dir).resolve()
|
||||
path = directory / filename
|
||||
if path.is_symlink() or not path.is_file():
|
||||
raise HTTPException(status_code=404, detail="Recording not found")
|
||||
return path
|
||||
|
||||
|
||||
@router.get("/api/recordings")
|
||||
def list_recordings(_: Auth) -> list[dict[str, object]]:
|
||||
directory = Path(config.recordings_dir)
|
||||
if not directory.is_dir():
|
||||
return []
|
||||
|
||||
rows: list[dict[str, object]] = []
|
||||
for path in directory.glob("*.mp4"):
|
||||
if path.is_symlink():
|
||||
continue
|
||||
try:
|
||||
stat_result = path.stat()
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
rows.append(
|
||||
{
|
||||
"name": path.name,
|
||||
"size_bytes": stat_result.st_size,
|
||||
"created_at": datetime.fromtimestamp(
|
||||
stat_result.st_mtime, tz=UTC
|
||||
).isoformat(),
|
||||
}
|
||||
)
|
||||
rows.sort(key=lambda row: str(row["created_at"]), reverse=True)
|
||||
return rows
|
||||
|
||||
|
||||
@router.get("/api/recordings/{filename}")
|
||||
def download_recording(filename: str, _: Auth) -> FileResponse:
|
||||
path = _recording_path(filename)
|
||||
return FileResponse(path, media_type="video/mp4", filename=path.name)
|
||||
|
||||
|
||||
@router.delete("/api/recordings/{filename}", status_code=204)
|
||||
def delete_recording(filename: str, _: Auth) -> None:
|
||||
path = _recording_path(filename)
|
||||
try:
|
||||
path.unlink()
|
||||
except FileNotFoundError:
|
||||
raise HTTPException(status_code=404, detail="Recording not found") from None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Log viewer
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user