Merge pull request #263 from naturallaw777/copilot/add-restart-button-to-service-modal
Add safe service restart action to Service Detail modal
This commit is contained in:
@@ -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.DEVNULL,
|
||||
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:
|
||||
|
||||
@@ -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 ──────────────────────────────────────── */
|
||||
|
||||
|
||||
@@ -270,6 +270,15 @@ async function openServiceDetailModal(unit, name, icon) {
|
||||
'</div>';
|
||||
}
|
||||
|
||||
if (effectiveEnabled || data.enabled) {
|
||||
html += '<div class="svc-detail-section svc-detail-restart-section">' +
|
||||
'<div class="svc-detail-section-title">Troubleshooting</div>' +
|
||||
'<p class="svc-detail-desc">If you\'re experiencing issues with this service, try restarting it.</p>' +
|
||||
'<button class="btn btn-warning svc-detail-restart-btn" id="svc-detail-restart-btn">🔄 Restart Service</button>' +
|
||||
'<div class="svc-detail-restart-result" id="svc-detail-restart-result"></div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
$credsBody.innerHTML = html;
|
||||
_attachCopyHandlers($credsBody);
|
||||
|
||||
@@ -296,6 +305,34 @@ 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…";
|
||||
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);
|
||||
}, RESTART_REFRESH_DELAY_MS);
|
||||
} catch (e) {
|
||||
restartResult.classList.add("error");
|
||||
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";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
||||
Reference in New Issue
Block a user