Files
attack-defense-platform/panel/static/login.html
T
MythEclipse c6fd9ec268 feat: challenge registry-driven platform + XVI/XVII imports + admin toggle + domain rename
- Rename repo/domain: attack-defense-platform / attackdefense.imrnes.team (all refs replaced)
- challenge_registry.json: single source of truth (28 challs across gemastik18/xvi/xvii)
- teams.py: registry-driven CHALLENGES, set_challenge_enabled, sync_challenge_runtime
  (apply enable/disable to live teams: build/up or stop/remove + receiver restart)
- compose_gen.py: render per-team compose from canonical per-challenge templates
  (image reuse, per-team ports 30xxx, flag mounts, passwords)
- gen_canonical_composes.py: canonical docker-compose.yml for all services
- import_new_challenges.py: import XVI/XVII services + EOL base image fixes
  (debian:buster→bookworm, node:14→20, python:3.7-slim→3.11)
- receiver: xvi package (10 checkers) + xvii package (12 generic checkers),
  Challenge base reads PASSWORD_<team_port> from env; gen_receiver_main.py
  generates per-team main.py from registry
- main.py: /api/challenges returns full registry; PATCH /api/challenges/<name>
  toggles enabled + applies to live teams
- index.html: 🏗️ Challenge Manager tab (toggle per challenge, grouped by set)
- SLA bonus now dynamic (all enabled challenges, not hardcoded 6)
2026-09-25 14:04:33 +08:00

64 lines
2.4 KiB
HTML

<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Attack Defense Platform — Login</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body {
font-family: 'JetBrains Mono','Fira Code',monospace;
background: radial-gradient(ellipse at top, #0d1b2a 0%, #0a0f1c 60%, #05070d 100%);
color: #c9d4e3; height:100vh; display:flex; align-items:center; justify-content:center;
}
.card {
background:#0e1526cc; border:1px solid #1e3a5f; border-radius:14px;
padding:42px 46px; width:360px; box-shadow:0 0 60px #0a3d6b55;
}
h1 { font-size:20px; color:#5ad1ff; letter-spacing:1px; margin-bottom:6px; }
.sub { color:#718096; font-size:12px; margin-bottom:28px; }
label { display:block; font-size:11px; color:#7d8ba1; margin:14px 0 6px; text-transform:uppercase; letter-spacing:1px; }
input {
width:100%; background:#0a101f; border:1px solid #1e3a5f; color:#dbe6f4;
border-radius:8px; padding:11px 13px; font-size:14px; font-family:inherit; outline:none;
}
input:focus { border-color:#3b82f6; box-shadow:0 0 0 3px #3b82f633; }
button {
width:100%; margin-top:24px; background:linear-gradient(135deg,#0ea5e9,#2563eb);
border:none; color:#fff; font-family:inherit; font-weight:700; font-size:14px;
padding:12px; border-radius:8px; cursor:pointer; letter-spacing:.5px; transition:filter .2s;
}
button:hover { filter:brightness(1.15); }
.err { color:#f87171; font-size:12px; margin-top:14px; min-height:16px; }
</style>
</head>
<body>
<div class="card">
<h1>⚔️ ATTACK DEFENSE</h1>
<div class="sub">Node Control Panel — imrnes</div>
<form id="f">
<label>Username</label>
<input id="u" autocomplete="username" required>
<label>Password</label>
<input id="p" type="password" autocomplete="current-password" required>
<button type="submit">MASUK</button>
<div class="err" id="err"></div>
</form>
</div>
<script>
const f = document.getElementById('f');
f.addEventListener('submit', async (e) => {
e.preventDefault();
const err = document.getElementById('err');
err.textContent = '';
const r = await fetch('/api/login', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({user: u.value, pass: p.value})
});
if (r.ok) location.href = '/';
else err.textContent = 'Kredensial salah!';
});
</script>
</body>
</html>