From fce46086476559c07ecc800d2d1e7fd3ec00f4e2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:13:07 +0000 Subject: [PATCH] Add service restart API and modal restart action Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/8e6c98f7-8b24-4ec0-944b-0310e0989495 Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com> --- app/sovran_systemsos_web/server.py | 29 +++++++++++++ app/sovran_systemsos_web/static/css/tiles.css | 41 +++++++++++++++++++ .../static/js/service-detail.js | 36 ++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index 09e8b88..7b1b842 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -2963,6 +2963,35 @@ async def api_ping(): return {"ok": True} +@app.post("/api/service/{unit}/restart") +async def api_service_restart(unit: str): + cfg = load_config() + services = cfg.get("services", []) + allowed_units = { + str(s.get("unit", "")).strip() + for s in services + if s.get("unit") + } + if unit not in allowed_units: + raise HTTPException(status_code=404, detail="Service not found") + + try: + proc = await asyncio.create_subprocess_exec( + "systemctl", "restart", unit, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + _, stderr = await proc.communicate() + except Exception as exc: + raise HTTPException(status_code=500, detail=f"Failed to restart service: {exc}") + + if proc.returncode != 0: + detail = stderr.decode(errors="ignore").strip() or "systemctl restart failed" + raise HTTPException(status_code=500, detail=detail) + + return {"ok": True} + + @app.post("/api/reboot") async def api_reboot(): try: diff --git a/app/sovran_systemsos_web/static/css/tiles.css b/app/sovran_systemsos_web/static/css/tiles.css index efc8d77..32f2278 100644 --- a/app/sovran_systemsos_web/static/css/tiles.css +++ b/app/sovran_systemsos_web/static/css/tiles.css @@ -353,6 +353,47 @@ font-weight: 600; } +.btn-warning { + background-color: #d97706; + color: #fff; +} + +.btn-warning:hover:not(:disabled) { + background-color: #b45309; +} + +.svc-detail-restart-section { + border-top: 1px solid var(--border-color); + padding-top: 16px; +} + +.svc-detail-restart-btn { + margin-top: 8px; +} + +.svc-detail-restart-result { + margin-top: 12px; + padding: 12px 16px; + border-radius: 8px; + font-size: 0.88rem; + line-height: 1.5; + display: none; +} + +.svc-detail-restart-result.success { + background-color: rgba(109, 191, 139, 0.12); + border: 1px solid var(--green); + color: var(--green); + display: block; +} + +.svc-detail-restart-result.error { + background-color: rgba(239, 68, 68, 0.12); + border: 1px solid #ef4444; + color: #f87171; + display: block; +} + /* ── Desktop launch buttons ──────────────────────────────────────── */ diff --git a/app/sovran_systemsos_web/static/js/service-detail.js b/app/sovran_systemsos_web/static/js/service-detail.js index 4d8effc..0320556 100644 --- a/app/sovran_systemsos_web/static/js/service-detail.js +++ b/app/sovran_systemsos_web/static/js/service-detail.js @@ -270,6 +270,15 @@ async function openServiceDetailModal(unit, name, icon) { ''; } + if (effectiveEnabled || data.enabled) { + html += '
If you\'re experiencing issues with this service, try restarting it.
' + + '' + + '' + + '