slick-relay / static /login.html
Vincent Kaufmann
Initial SLICK relay deployment
81849d9
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SLICK — Login</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #0d0d0d;
color: #e0e0e0;
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', 'Courier New', monospace;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.login-box {
background: #1a1a1a;
border: 1px solid #2a2a2a;
border-radius: 16px;
padding: 2.5rem;
width: 90%;
max-width: 380px;
text-align: center;
}
h1 {
color: #ffb300;
font-size: 1.5rem;
letter-spacing: 0.3em;
margin-bottom: 0.5rem;
}
.subtitle {
color: #555;
font-size: 0.7rem;
margin-bottom: 2rem;
letter-spacing: 0.1em;
}
input {
width: 100%;
background: #0d0d0d;
border: 1px solid #333;
color: #e0e0e0;
padding: 0.9rem 1rem;
font-family: inherit;
font-size: 0.8rem;
border-radius: 8px;
outline: none;
margin-bottom: 1rem;
text-align: center;
letter-spacing: 0.05em;
}
input:focus { border-color: #ffb300; }
input::placeholder { color: #444; }
button {
width: 100%;
background: #ffb300;
color: #0d0d0d;
border: none;
padding: 0.9rem;
font-family: inherit;
font-size: 0.85rem;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
letter-spacing: 0.05em;
}
button:hover { background: #ffc107; }
button:disabled { background: #333; color: #666; cursor: not-allowed; }
.error {
color: #f44336;
font-size: 0.75rem;
margin-top: 0.8rem;
min-height: 1.2rem;
}
.status-dot {
display: inline-block;
width: 8px; height: 8px;
border-radius: 50%;
background: #ffb300;
margin-right: 0.4rem;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
</style>
</head>
<body>
<div class="login-box">
<h1><span class="status-dot"></span>S L I C K</h1>
<div class="subtitle">REMOTE RELAY</div>
<input type="password" id="token-input" placeholder="Paste access token" autocomplete="off" autofocus>
<button id="login-btn" onclick="doLogin()">AUTHENTICATE</button>
<div class="error" id="error-msg"></div>
</div>
<script>
const input = document.getElementById('token-input');
const btn = document.getElementById('login-btn');
const errorMsg = document.getElementById('error-msg');
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') doLogin();
});
async function doLogin() {
const token = input.value.trim();
if (!token) return;
btn.disabled = true;
btn.textContent = 'VERIFYING...';
errorMsg.textContent = '';
try {
const res = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token })
});
const data = await res.json();
if (data.ok) {
window.location.href = '/';
} else {
errorMsg.textContent = 'Invalid token. Try again.';
btn.disabled = false;
btn.textContent = 'AUTHENTICATE';
input.value = '';
input.focus();
}
} catch (e) {
errorMsg.textContent = 'Connection error: ' + e.message;
btn.disabled = false;
btn.textContent = 'AUTHENTICATE';
}
}
</script>
</body>
</html>