🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
{% extends "admin/layout.html" %}
{% block admin_title %}Usługi{% endblock %}
{% block admin_h1 %} Usługi (systemd){% endblock %}
{% block admin_sub %}Lista, start/stop/restart/enable/disable{% endblock %}
{% block admin_head %}
<style>
.svc-filter { margin-bottom:0.8rem; display:flex; gap:0.5rem; flex-wrap:wrap; }
.svc-filter input { flex:1; min-width:200px; }
.svc-table { width:100%; font-size:0.82rem; border-collapse:collapse; }
.svc-table th { position:sticky; top:0; background:#161b22; padding:0.4rem 0.5rem; text-align:left;
border-bottom:2px solid var(--border); color:var(--muted); font-weight:600; }
.svc-table td { padding:0.3rem 0.5rem; border-bottom:1px solid #111; }
.svc-table tr:hover { background:#111; }
.dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:6px; }
.dot.green { background:#3fb950; } .dot.red { background:#f85149; } .dot.yellow { background:#d29922; }
.svc-btn { padding:0.15rem 0.5rem; font-size:0.72rem; border-radius:4px; cursor:pointer;
border:1px solid var(--border); background:var(--bg); color:var(--fg); }
.svc-btn:hover { background:#161b22; }
.svc-out { white-space:pre-wrap; font-family:'JetBrains Mono',monospace; font-size:0.75rem;
background:#0a0a0a; border:1px solid #1a1a2e; border-radius:var(--radius-sm);
padding:0.6rem; margin-top:0.6rem; max-height:280px; overflow:auto; display:none; }
</style>
{% endblock %}
{% block admin_content %}
<div class="svc-filter">
<input id="svc-q" placeholder="Filtruj (np. nginx, pag, ssh)..." oninput="render()">
<button class="btn btn-sm" onclick="loadServices()"> Odśwież</button>
</div>
<table class="svc-table"><thead><tr>
<th>Usługa</th><th>Aktywna</th><th>Pod-stan</th><th>Akcje</th>
</tr></thead><tbody id="svc-tbody"></tbody></table>
<div class="svc-out" id="svc-out"></div>
<script>
let SVC=[];
async function loadServices(){
try{ const r=await fetch('/admin/services/api'); const d=await r.json(); SVC=d.services;
render(); }catch(e){}
}
function render(){
const q=document.getElementById('svc-q').value.toLowerCase();
const tb=document.getElementById('svc-tbody'); let html='';
SVC.filter(s=>s.name.toLowerCase().includes(q)).forEach(s=>{
const act = s.active==='active'?'<span class="dot green"></span>aktywna'
: s.active==='failed'?'<span class="dot red"></span>padła'
: '<span class="dot yellow"></span>'+s.active;
html+=`<tr><td><code>${s.name}</code></td><td>${act}</td><td>${s.sub}</td><td>
<button class="svc-btn" onclick="act('${s.name}','start')">▶ start</button>
<button class="svc-btn" onclick="act('${s.name}','stop')">⏹ stop</button>
<button class="svc-btn" onclick="act('${s.name}','restart')"> restart</button>
<button class="svc-btn" onclick="act('${s.name}','reload')">↻ reload</button>
<button class="svc-btn" onclick="act('${s.name}','enable')"> enable</button>
<button class="svc-btn" onclick="act('${s.name}','disable')"> disable</button>
<button class="svc-btn" onclick="act('${s.name}','status')">status</button>
</td></tr>`;
});
tb.innerHTML = html || '<tr><td colspan="4" class="dim">brak usług</td></tr>';
}
async function act(name, a){
const out=document.getElementById('svc-out');
const fd=new FormData(); fd.append('name',name); fd.append('action',a);
try{
const r=await fetch('/admin/services/action',{method:'POST',body:fd,headers:{'X-CSRF-Token':CSRF_TOKEN}});
const d=await r.json();
out.style.display='block';
out.textContent = d.output || (d.ok?'OK':'błąd');
if(d.active!==undefined) out.textContent += `\n→ stan: ${d.active}`;
if(a!=='status') setTimeout(loadServices, 1200);
}catch(e){ out.style.display='block'; out.textContent='błąd: '+e; }
}
loadServices();
</script>
{% endblock %}