Dateien
AegisSight-Globe/static/login.html
Claude Dev 338e082467 Auth: Magic Link Login + Globe-Zugangssteuerung
- Magic Link Login (E-Mail + 6-stelliger Code)
- JWT-basierte Session (24h)
- Prueft: is_active=1 UND globe_access=1
- Akzeptiert auch Monitor-JWT-Tokens (Kompatibilitaet)
- Globe-spezifisches E-Mail-Template (Dark Theme)
- Alle Daten-APIs hinter Auth-Middleware
- Login-Seite mit taktischem Design
- Auto-Redirect bei fehlendem/abgelaufenem Token
- Fetch-Wrapper injiziert Authorization Header automatisch
2026-03-24 11:57:00 +01:00

153 Zeilen
7.1 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AegisSight Globe — Login</title>
<style>
:root { --bg: #0b1121; --accent: #00ff88; --border: rgba(0,255,136,0.15); --text: #e8eaf0; --text-dim: rgba(255,255,255,0.5); --mono: 'JetBrains Mono','Courier New',monospace; }
* { margin:0; padding:0; box-sizing:border-box; }
body { min-height:100vh; background:var(--bg); color:var(--text); font-family:var(--mono); display:flex; align-items:center; justify-content:center; }
.login-box { width:380px; background:rgba(11,17,33,0.95); border:1px solid var(--border); border-radius:12px; padding:40px 32px; box-shadow:0 16px 64px rgba(0,0,0,0.5); }
.logo { text-align:center; margin-bottom:32px; }
.logo svg { width:40px; height:40px; }
.logo h1 { font-size:14px; letter-spacing:3px; color:var(--accent); margin-top:12px; }
.logo p { font-size:11px; color:var(--text-dim); margin-top:4px; }
.form-group { margin-bottom:20px; }
label { display:block; font-size:10px; letter-spacing:1.5px; color:var(--text-dim); margin-bottom:6px; text-transform:uppercase; }
input[type="email"], input[type="text"] {
width:100%; padding:12px 14px; background:rgba(255,255,255,0.05); border:1px solid var(--border);
border-radius:6px; color:var(--text); font-family:var(--mono); font-size:14px; outline:none; transition:border-color 0.2s;
}
input:focus { border-color:var(--accent); }
.code-input { text-align:center; letter-spacing:8px; font-size:24px; font-weight:700; }
.btn {
width:100%; padding:12px; background:rgba(0,255,136,0.1); border:1px solid var(--accent);
border-radius:6px; color:var(--accent); font-family:var(--mono); font-size:13px; font-weight:700;
letter-spacing:1px; cursor:pointer; transition:all 0.2s;
}
.btn:hover { background:rgba(0,255,136,0.2); }
.btn:disabled { opacity:0.4; cursor:not-allowed; }
.error { color:#ff4444; font-size:12px; margin-top:8px; display:none; }
.success { color:var(--accent); font-size:12px; margin-top:8px; display:none; }
.step { display:none; }
.step.active { display:block; }
.back-link { display:block; text-align:center; margin-top:16px; font-size:11px; color:var(--text-dim); cursor:pointer; }
.back-link:hover { color:var(--accent); }
</style>
</head>
<body>
<div class="login-box">
<div class="logo">
<svg viewBox="0 0 24 24" fill="none" stroke="#00ff88" stroke-width="1.5">
<circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10A15.3 15.3 0 0 1 12 2z"/>
</svg>
<h1>AEGISSIGHT GLOBE</h1>
<p>Geospatial Intelligence Dashboard</p>
</div>
<!-- Step 1: E-Mail -->
<div id="step-email" class="step active">
<div class="form-group">
<label>E-Mail-Adresse</label>
<input type="email" id="input-email" placeholder="name@beispiel.de" autofocus>
</div>
<button class="btn" id="btn-send" onclick="requestLink()">Zugangscode anfordern</button>
<div class="error" id="error-email"></div>
</div>
<!-- Step 2: Code -->
<div id="step-code" class="step">
<div class="form-group">
<label>6-stelliger Zugangscode</label>
<input type="text" id="input-code" class="code-input" maxlength="6" placeholder="------" inputmode="numeric" pattern="[0-9]*">
</div>
<div class="success" id="success-code" style="display:block;margin-bottom:16px;">Zugangscode wurde per E-Mail gesendet.</div>
<button class="btn" id="btn-verify" onclick="verifyCode()">Verifizieren</button>
<div class="error" id="error-code"></div>
<span class="back-link" onclick="showStep('email')">Andere E-Mail verwenden</span>
</div>
</div>
<script>
// Check if already logged in
if (localStorage.getItem('globe_token')) {
window.location.href = '/';
}
function showStep(step) {
document.querySelectorAll('.step').forEach(function(el) { el.classList.remove('active'); });
document.getElementById('step-' + step).classList.add('active');
document.querySelectorAll('.error').forEach(function(el) { el.style.display = 'none'; });
}
function showError(id, msg) {
var el = document.getElementById(id);
el.textContent = msg;
el.style.display = 'block';
}
async function requestLink() {
var email = document.getElementById('input-email').value.trim();
if (!email) return;
var btn = document.getElementById('btn-send');
btn.disabled = true;
btn.textContent = 'Sende...';
document.getElementById('error-email').style.display = 'none';
try {
var resp = await fetch('/api/auth/request-link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email }),
});
var data = await resp.json();
if (!resp.ok) {
showError('error-email', data.detail || 'Fehler');
} else {
showStep('code');
document.getElementById('input-code').focus();
}
} catch (e) {
showError('error-email', 'Verbindungsfehler');
}
btn.disabled = false;
btn.textContent = 'Zugangscode anfordern';
}
async function verifyCode() {
var code = document.getElementById('input-code').value.trim();
var email = document.getElementById('input-email').value.trim();
if (!code || code.length !== 6) return;
var btn = document.getElementById('btn-verify');
btn.disabled = true;
btn.textContent = 'Pruefe...';
document.getElementById('error-code').style.display = 'none';
try {
var resp = await fetch('/api/auth/verify-code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email, code: code }),
});
var data = await resp.json();
if (!resp.ok) {
showError('error-code', data.detail || 'Fehler');
} else {
localStorage.setItem('globe_token', data.token);
window.location.href = '/';
}
} catch (e) {
showError('error-code', 'Verbindungsfehler');
}
btn.disabled = false;
btn.textContent = 'Verifizieren';
}
// Enter-Taste
document.getElementById('input-email').addEventListener('keydown', function(e) { if (e.key === 'Enter') requestLink(); });
document.getElementById('input-code').addEventListener('keydown', function(e) { if (e.key === 'Enter') verifyCode(); });
</script>
</body>
</html>