"""What is running here, and getting to what is not. Read `services/updates.py` first — the reason the button writes a file rather than doing the work is there, and it is the whole design. """ from __future__ import annotations import logging from fastapi import APIRouter, Request, Response, status from fastapi.responses import RedirectResponse from lembas.api.deps import AdminUser, Db from lembas.services import updates as updates_service from lembas.web.templating import render log = logging.getLogger(__name__) router = APIRouter(prefix="/admin/updates", tags=["admin-updates"]) def _page(request: Request, state, saved: str = "") -> Response: return render( request, "admin/updates.html", { "state": state, "command": updates_service.manual_command(), "saved": saved, }, ) @router.get("") async def updates_page(request: Request, db: Db, user: AdminUser, saved: str = ""): """No network on a page load. `read(fetch=False)` compares against whatever the last fetch left behind, so opening this is a few git reads off the local disk. A page that reached the remote every time it was rendered would be one somebody stops opening. """ return _page(request, updates_service.read(), saved) @router.post("/check") async def check(request: Request, db: Db, user: AdminUser) -> Response: """Ask the remote what is there. The one place this touches the network.""" state = updates_service.read(fetch=True) log.info("%s checked for updates", user.email) return _page(request, state) @router.post("/apply") async def apply(db: Db, user: AdminUser) -> Response: """Write the request the helper is watching for. Refused when the helper is not installed rather than written and left to sit there: a file nothing is watching is a button that reports success and does nothing, which is the failure this codebase keeps cataloguing. """ if not updates_service.helper_installed(): return RedirectResponse( "/admin/updates?saved=The+update+helper+is+not+installed+on+this+host.", status_code=status.HTTP_303_SEE_OTHER, ) problem = updates_service.request_update(user.email) message = problem or "Update requested. The service will restart in a moment." return RedirectResponse( f"/admin/updates?saved={message.replace(' ', '+')}", status_code=status.HTTP_303_SEE_OTHER, ) @router.post("/cancel") async def cancel(db: Db, user: AdminUser) -> Response: updates_service.clear_request() return RedirectResponse( "/admin/updates?saved=Request+withdrawn.", status_code=status.HTTP_303_SEE_OTHER )