feat: SLA dashboard per team + points system (100/flag, +50 SLA bonus) + badges juara/runner-up/3rd + scoreboard API public+admin
Panel: /api/scoreboard + /api/public/scoreboard; teams.py: points.json ledger, probe_team_sla_fast, sla_status_all w/ background refresher; team.html: SLA & Skor tab; index.html: admin SLA tab; leaderboard shows points; Phew checker timeouts raised for slow Paillier keygen
This commit is contained in:
+48
-1
@@ -118,6 +118,7 @@
|
||||
<button data-view="submit" onclick="showView('submit')">🚩 Submit Flag</button>
|
||||
<button data-view="activity" onclick="showView('activity')">📡 Aktivitas</button>
|
||||
<button data-view="leaderboard" onclick="showView('leaderboard')">🏆 Leaderboard</button>
|
||||
<button data-view="sla" onclick="showView('sla'); loadSla()">📊 SLA & Skor</button>
|
||||
<button data-view="guide" onclick="showView('guide')">📖 Panduan SSH</button>
|
||||
</div>
|
||||
|
||||
@@ -194,6 +195,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="view" id="view-sla">
|
||||
<div class="card">
|
||||
<h2>📊 SLA & Skor Tim</h2>
|
||||
<p style="font-size:13px;color:var(--dim);margin-bottom:12px">
|
||||
💚 <b>SLA</b> = service timmu hidup (dicek tiap ~30 detik) · 🏆 <b>poin</b> = dapat dari mencuri flag lawan (+100/flag) & bonus SLA saat seluruh service UP (+50).
|
||||
</p>
|
||||
<div id="slaList"><div class="lb-empty">Memuat…</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="view" id="view-guide">
|
||||
<div class="card guide">
|
||||
<h2>📖 Panduan SSH & Attack</h2>
|
||||
@@ -254,6 +265,7 @@ function showView(v) {
|
||||
if (v === 'targets') loadTargets();
|
||||
if (v === 'activity') loadActivity();
|
||||
if (v === 'leaderboard') loadLeaderboard();
|
||||
if (v === 'sla') loadSla();
|
||||
}
|
||||
|
||||
// ---------- auth ----------
|
||||
@@ -425,6 +437,41 @@ async function loadActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- SLA & scoreboard (public endpoint) ----------
|
||||
async function loadSla() {
|
||||
const box = document.getElementById('slaList');
|
||||
try {
|
||||
const d = await api('/api/public/scoreboard');
|
||||
const teams = d.teams || [];
|
||||
if (!teams.length) {
|
||||
box.innerHTML = '<div class="lb-empty">Belum ada data SLA.</div>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = teams.map(t => {
|
||||
const isMe = t.team === TEAM_ID;
|
||||
const pct = Math.round(t.sla_pct || 0);
|
||||
const color = pct === 100 ? '#2ecc71' : (pct >= 60 ? '#f1c40f' : '#e74c3c');
|
||||
const bar = `<div style="height:8px;background:#1e2a3a;border-radius:4px;margin:6px 0;overflow:hidden"><div style="height:100%;width:${pct}%;background:${color};transition:width .5s"></div></div>`;
|
||||
return `<div class="lb-row ${isMe ? 'me' : ''}">
|
||||
<span class="lb-rank" style="width:34px">${esc(t.badge || t.rank)}</span>
|
||||
<span class="lb-name">${esc(t.label)} ${isMe ? '<small>(kamu)</small>' : ''}</span>
|
||||
<span class="lb-solves" style="width:170px;text-align:right">
|
||||
<b style="color:${color}">${t.alive}/${t.total} (${pct}%)</b>
|
||||
<br><small style="color:var(--dim)">⚑ ${t.solves} flag · 🏆 ${t.points} pts</small>
|
||||
</span>
|
||||
</div>${bar}`;
|
||||
}).join('');
|
||||
} catch (e) {
|
||||
box.innerHTML = '<div class="lb-empty">Gagal memuat SLA.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// auto-refresh SLA while its tab is open (every 30s)
|
||||
setInterval(async () => {
|
||||
const s = document.getElementById('view-sla');
|
||||
if (s && s.classList.contains('active')) loadSla();
|
||||
}, 30000);
|
||||
|
||||
// ---------- leaderboard (public endpoint) ----------
|
||||
async function loadLeaderboard() {
|
||||
const box = document.getElementById('lbList');
|
||||
@@ -442,7 +489,7 @@ async function loadLeaderboard() {
|
||||
return `<div class="lb-row ${isMe ? 'me' : ''}">
|
||||
<span class="lb-rank">${rank}</span>
|
||||
<span class="lb-name">${esc(t.name)} ${isMe ? '<small>(kamu)</small>' : ''}</span>
|
||||
<span class="lb-solves">⚑ ${t.solves} solve${t.solves > 1 ? 's' : ''}</span>
|
||||
<span class="lb-solves">⚑ ${t.solves} solve${t.solves > 1 ? 's' : ''} · 🏆 ${t.points ?? 0} pts</span>
|
||||
</div>`;
|
||||
}).join('');
|
||||
const found = teams.some(t => t.team === TEAM_ID);
|
||||
|
||||
Reference in New Issue
Block a user