From 8fd08057d8ddb56a1820c4b045b89e5bfa43a780 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 16 Apr 2026 14:58:54 +0000
Subject: [PATCH 1/4] Initial plan
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 2/4] 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 += '
' +
+ '
Troubleshooting
' +
+ '
If you\'re experiencing issues with this service, try restarting it.
' +
+ '
' +
+ '
' +
+ '
';
+ }
+
$credsBody.innerHTML = html;
_attachCopyHandlers($credsBody);
@@ -296,6 +305,33 @@ async function openServiceDetailModal(unit, name, icon) {
}
}
+ var restartBtn = document.getElementById("svc-detail-restart-btn");
+ var restartResult = document.getElementById("svc-detail-restart-result");
+ if (restartBtn && restartResult) {
+ restartBtn.addEventListener("click", async function() {
+ restartBtn.disabled = true;
+ restartBtn.textContent = "Restarting…";
+ restartResult.className = "svc-detail-restart-result";
+ restartResult.textContent = "";
+
+ try {
+ await apiFetch("/api/service/" + encodeURIComponent(unit) + "/restart", { method: "POST" });
+ restartResult.classList.add("success");
+ restartResult.textContent = "✅ Service restarted successfully.";
+ restartBtn.disabled = false;
+ restartBtn.textContent = "🔄 Restart Service";
+ setTimeout(function() {
+ openServiceDetailModal(unit, name, icon);
+ }, 3000);
+ } catch (e) {
+ restartResult.classList.add("error");
+ restartResult.textContent = e && e.message ? e.message : "Failed to restart service.";
+ restartBtn.disabled = false;
+ restartBtn.textContent = "🔄 Restart Service";
+ }
+ });
+ }
+
// Configure / Reconfigure Domain buttons (for non-feature services that need a domain)
var configDomainBtn = document.getElementById("svc-detail-config-domain-btn");
var reconfigDomainBtn = document.getElementById("svc-detail-reconfig-domain-btn");
From c37816d257ed97a1dd922b1c3348b95851d416b2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 16 Apr 2026 15:18:34 +0000
Subject: [PATCH 3/4] Address review nits for restart flow
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 | 2 +-
app/sovran_systemsos_web/static/js/service-detail.js | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py
index 7b1b842..63eb587 100644
--- a/app/sovran_systemsos_web/server.py
+++ b/app/sovran_systemsos_web/server.py
@@ -2978,7 +2978,7 @@ async def api_service_restart(unit: str):
try:
proc = await asyncio.create_subprocess_exec(
"systemctl", "restart", unit,
- stdout=asyncio.subprocess.PIPE,
+ stdout=asyncio.subprocess.DEVNULL,
stderr=asyncio.subprocess.PIPE,
)
_, stderr = await proc.communicate()
diff --git a/app/sovran_systemsos_web/static/js/service-detail.js b/app/sovran_systemsos_web/static/js/service-detail.js
index 0320556..ccce67f 100644
--- a/app/sovran_systemsos_web/static/js/service-detail.js
+++ b/app/sovran_systemsos_web/static/js/service-detail.js
@@ -308,6 +308,7 @@ async function openServiceDetailModal(unit, name, icon) {
var restartBtn = document.getElementById("svc-detail-restart-btn");
var restartResult = document.getElementById("svc-detail-restart-result");
if (restartBtn && restartResult) {
+ var RESTART_REFRESH_DELAY_MS = 3000;
restartBtn.addEventListener("click", async function() {
restartBtn.disabled = true;
restartBtn.textContent = "Restarting…";
@@ -322,7 +323,7 @@ async function openServiceDetailModal(unit, name, icon) {
restartBtn.textContent = "🔄 Restart Service";
setTimeout(function() {
openServiceDetailModal(unit, name, icon);
- }, 3000);
+ }, RESTART_REFRESH_DELAY_MS);
} catch (e) {
restartResult.classList.add("error");
restartResult.textContent = e && e.message ? e.message : "Failed to restart service.";
From b5e89c38f8809d6eebce4cced58a1fa0e8537370 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 16 Apr 2026 15:19:16 +0000
Subject: [PATCH 4/4] Improve restart fallback error message
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/static/js/service-detail.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/sovran_systemsos_web/static/js/service-detail.js b/app/sovran_systemsos_web/static/js/service-detail.js
index ccce67f..1bde524 100644
--- a/app/sovran_systemsos_web/static/js/service-detail.js
+++ b/app/sovran_systemsos_web/static/js/service-detail.js
@@ -326,7 +326,7 @@ async function openServiceDetailModal(unit, name, icon) {
}, RESTART_REFRESH_DELAY_MS);
} catch (e) {
restartResult.classList.add("error");
- restartResult.textContent = e && e.message ? e.message : "Failed to restart service.";
+ restartResult.textContent = e && e.message ? e.message : "Failed to restart service. Please check service logs and try again.";
restartBtn.disabled = false;
restartBtn.textContent = "🔄 Restart Service";
}