68 lines
1.9 KiB
Python
68 lines
1.9 KiB
Python
from typing import Any
|
|
|
|
from fastapi import APIRouter, Request
|
|
from fastapi.responses import HTMLResponse
|
|
|
|
from app import journal
|
|
from app.render import render
|
|
|
|
router = APIRouter(prefix="/api", tags=["journal"])
|
|
|
|
|
|
@router.get("/journal")
|
|
async def journal_view(
|
|
_request: Request,
|
|
level: str = "all",
|
|
unit: str = "",
|
|
search: str = "",
|
|
cursor: str = "",
|
|
hide_sudo: str = "",
|
|
):
|
|
"""Render the Journal tab fragment: a page of journal entries.
|
|
|
|
Without a cursor it fetches 100 lines; with one (continuing a scroll)
|
|
200, then keeps the newest 400 for the template. level is validated
|
|
against journal.LEVELS, and failures (RuntimeError/OSError from
|
|
journalctl) are rendered as an error banner instead of a 500.
|
|
|
|
Args:
|
|
_request: FastAPI request (unused).
|
|
level: "all" / "warn" / "err".
|
|
unit: unit name filter, empty for none.
|
|
search: free-text filter, empty for none.
|
|
cursor: journal cursor to continue after, empty for none.
|
|
hide_sudo: "on" to hide sudo's own log entries.
|
|
|
|
Returns:
|
|
The rendered journal.html as an HTMLResponse.
|
|
"""
|
|
if level not in journal.LEVELS:
|
|
level = "all"
|
|
lines = 200 if cursor else 100
|
|
error = None
|
|
entries: list[dict[str, Any]] = []
|
|
next_cursor = ""
|
|
try:
|
|
entries, nc = await journal.tail(
|
|
cursor or None,
|
|
level,
|
|
unit or None,
|
|
search or None,
|
|
lines,
|
|
hide_sudo=(hide_sudo == "on"),
|
|
)
|
|
next_cursor = nc or ""
|
|
entries = entries[-400:]
|
|
except (RuntimeError, OSError) as e:
|
|
error = str(e)[:300]
|
|
return HTMLResponse(
|
|
render(
|
|
"journal.html",
|
|
entries=entries,
|
|
next_cursor=next_cursor,
|
|
level=level,
|
|
unit=unit,
|
|
search=search,
|
|
error=error,
|
|
)
|
|
)
|