Spaces:
Paused
Paused
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>Secure Terminal Access</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"> | |
| <style> | |
| /* --- CORE VARIABLES --- */ | |
| :root { | |
| --bg-deep: #0a0b10; | |
| --bg-surface: #12141d; | |
| --bg-card: rgba(22, 24, 34, 0.6); | |
| --brand-orange: #FF6B00; | |
| --brand-orange-light: #FFA600; | |
| --brand-glow: rgba(255, 107, 0, 0.4); | |
| --text-primary: #F8F9FA; | |
| --text-secondary: #8B949E; | |
| --input-bg: rgba(10, 11, 16, 0.8); | |
| --border-color: rgba(255, 255, 255, 0.06); | |
| --border-focus: rgba(255, 107, 0, 0.5); | |
| --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); | |
| --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); | |
| } | |
| /* --- RESET & BASE --- */ | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| -webkit-tap-highlight-color: transparent; | |
| } | |
| body { | |
| font-family: 'Outfit', sans-serif; | |
| background-color: var(--bg-deep); | |
| color: var(--text-primary); | |
| min-height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| overflow: hidden; | |
| position: relative; | |
| } | |
| /* --- DYNAMIC BACKGROUND & GRID --- */ | |
| .bg-grid { | |
| position: absolute; | |
| top: 0; left: 0; width: 100vw; height: 100vh; | |
| background-image: | |
| linear-gradient(to right, rgba(255,255,255,0.02) 1px, transparent 1px), | |
| linear-gradient(to bottom, rgba(255,255,255,0.02) 1px, transparent 1px); | |
| background-size: 40px 40px; | |
| z-index: 0; | |
| transform: perspective(500px) rotateX(60deg) translateY(-100px) translateZ(-200px); | |
| animation: gridMove 20s linear infinite; | |
| opacity: 0.5; | |
| } | |
| .ambient-light { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| width: 500px; | |
| height: 500px; | |
| background: radial-gradient(circle, var(--brand-glow) 0%, transparent 60%); | |
| filter: blur(80px); | |
| z-index: 1; | |
| animation: pulseLight 8s alternate infinite; | |
| } | |
| /* --- APP LAYOUT --- */ | |
| .app-container { | |
| width: 100%; | |
| max-width: 440px; | |
| position: relative; | |
| z-index: 10; | |
| padding: 24px; | |
| } | |
| /* --- MAIN LOGIN CARD --- */ | |
| .login-card { | |
| background: var(--bg-card); | |
| border-radius: 28px; | |
| padding: 45px 35px; | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border: 1px solid var(--border-color); | |
| box-shadow: 0 30px 60px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.05); | |
| position: relative; | |
| transform: translateY(30px); | |
| opacity: 0; | |
| animation: cardReveal 1s var(--ease-out-expo) forwards; | |
| } | |
| .login-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: -1px; left: -1px; right: -1px; bottom: -1px; | |
| border-radius: 28px; | |
| background: linear-gradient(135deg, var(--brand-orange-light), transparent 40%, transparent 60%, var(--brand-orange)); | |
| z-index: -1; | |
| opacity: 0; | |
| transition: opacity 0.5s ease; | |
| mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); | |
| -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); | |
| -webkit-mask-composite: xor; | |
| mask-composite: exclude; | |
| padding: 1px; | |
| } | |
| .login-card:hover::before { | |
| opacity: 0.5; | |
| } | |
| /* --- ADVANCED SVG LOGO --- */ | |
| .logo-wrapper { | |
| display: flex; | |
| justify-content: center; | |
| margin-bottom: 35px; | |
| position: relative; | |
| } | |
| .logo-core { | |
| position: relative; | |
| width: 85px; | |
| height: 85px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| animation: float 6s ease-in-out infinite; | |
| } | |
| .logo-svg { | |
| width: 100%; | |
| height: 100%; | |
| filter: drop-shadow(0 0 15px var(--brand-glow)); | |
| } | |
| .outer-hex { | |
| transform-origin: center; | |
| animation: spinRotate 20s linear infinite; | |
| } | |
| .inner-dash { | |
| stroke-dasharray: 10 5; | |
| animation: dashMove 2s linear infinite; | |
| } | |
| /* --- TYPOGRAPHY --- */ | |
| .header-section { | |
| text-align: center; | |
| margin-bottom: 40px; | |
| } | |
| h1 { | |
| font-size: 26px; | |
| font-weight: 600; | |
| letter-spacing: -0.5px; | |
| margin-bottom: 12px; | |
| background: linear-gradient(to right, #FFF, #A0A5B0); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| p { | |
| font-size: 15px; | |
| color: var(--text-secondary); | |
| font-weight: 400; | |
| } | |
| /* --- FORM & INPUTS --- */ | |
| .form-group { | |
| position: relative; | |
| margin-bottom: 30px; | |
| } | |
| .input-icon, .toggle-icon { | |
| position: absolute; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| color: var(--text-secondary); | |
| transition: all 0.3s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .input-icon { | |
| left: 18px; | |
| } | |
| .toggle-icon { | |
| right: 18px; | |
| cursor: pointer; | |
| background: none; | |
| border: none; | |
| padding: 5px; | |
| } | |
| .toggle-icon:hover { | |
| color: var(--text-primary); | |
| } | |
| .secure-input { | |
| width: 100%; | |
| background: var(--input-bg); | |
| border: 1.5px solid var(--border-color); | |
| border-radius: 16px; | |
| padding: 20px 55px 20px 50px; | |
| color: var(--brand-orange-light); | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 16px; | |
| letter-spacing: 2px; | |
| outline: none; | |
| transition: all 0.3s var(--ease-out-expo); | |
| box-shadow: inset 0 2px 10px rgba(0,0,0,0.5); | |
| } | |
| .secure-input::placeholder { | |
| color: rgba(139, 148, 158, 0.5); | |
| font-family: 'Outfit', sans-serif; | |
| letter-spacing: normal; | |
| } | |
| .secure-input:focus { | |
| border-color: var(--brand-orange); | |
| background: rgba(10, 11, 16, 0.95); | |
| box-shadow: 0 0 0 4px rgba(255, 107, 0, 0.1), inset 0 2px 10px rgba(0,0,0,0.5); | |
| } | |
| .secure-input:focus ~ .input-icon { | |
| color: var(--brand-orange); | |
| filter: drop-shadow(0 0 5px var(--brand-glow)); | |
| } | |
| /* --- SUBMIT BUTTON --- */ | |
| .btn-connect { | |
| width: 100%; | |
| background: linear-gradient(135deg, var(--brand-orange) 0%, #D85A00 100%); | |
| color: #fff; | |
| border: none; | |
| border-radius: 16px; | |
| padding: 20px; | |
| font-size: 17px; | |
| font-weight: 600; | |
| font-family: 'Outfit', sans-serif; | |
| cursor: pointer; | |
| position: relative; | |
| overflow: hidden; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 10px 25px rgba(255, 107, 0, 0.3); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .btn-connect::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; left: -100%; | |
| width: 50%; height: 100%; | |
| background: linear-gradient(to right, transparent, rgba(255,255,255,0.2), transparent); | |
| transform: skewX(-25deg); | |
| transition: all 0.5s ease; | |
| } | |
| .btn-connect:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 15px 35px rgba(255, 107, 0, 0.4); | |
| background: linear-gradient(135deg, var(--brand-orange-light) 0%, var(--brand-orange) 100%); | |
| } | |
| .btn-connect:hover::after { | |
| left: 200%; | |
| } | |
| .btn-connect:active { | |
| transform: translateY(1px); | |
| box-shadow: 0 5px 15px rgba(255, 107, 0, 0.3); | |
| } | |
| .loader-ring { | |
| display: none; | |
| width: 24px; height: 24px; | |
| border: 3px solid rgba(255,255,255,0.2); | |
| border-top: 3px solid #fff; | |
| border-radius: 50%; | |
| animation: spinRotate 0.8s linear infinite; | |
| } | |
| .btn-connect.is-loading .btn-text { display: none; } | |
| .btn-connect.is-loading .loader-ring { display: block; } | |
| .btn-connect.is-loading { | |
| pointer-events: none; | |
| opacity: 0.9; | |
| } | |
| /* --- SUCCESS OVERLAY --- */ | |
| .auth-success { | |
| position: absolute; | |
| top: 0; left: 0; width: 100%; height: 100%; | |
| background: rgba(18, 20, 29, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-radius: 28px; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: all 0.4s ease; | |
| z-index: 20; | |
| } | |
| .auth-success.active { | |
| opacity: 1; | |
| pointer-events: all; | |
| } | |
| .shield-check { | |
| width: 75px; | |
| height: 75px; | |
| color: #10B981; | |
| margin-bottom: 25px; | |
| stroke-dasharray: 100; | |
| stroke-dashoffset: 100; | |
| filter: drop-shadow(0 0 15px rgba(16, 185, 129, 0.4)); | |
| } | |
| .auth-success.active .shield-check { | |
| animation: drawCheck 1s var(--ease-out-expo) forwards; | |
| animation-delay: 0.2s; | |
| } | |
| .success-msg { | |
| color: #10B981; | |
| font-size: 22px; | |
| font-weight: 600; | |
| letter-spacing: 1.5px; | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| .auth-success.active .success-msg { | |
| animation: slideUpFade 0.5s ease forwards; | |
| animation-delay: 0.6s; | |
| } | |
| /* --- KEYFRAMES --- */ | |
| @keyframes gridMove { | |
| 0% { background-position: 0 0; } | |
| 100% { background-position: 0 40px; } | |
| } | |
| @keyframes pulseLight { | |
| 0% { opacity: 0.4; transform: translate(-50%, -50%) scale(1); } | |
| 100% { opacity: 0.7; transform: translate(-50%, -50%) scale(1.1); } | |
| } | |
| @keyframes cardReveal { | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-10px); } | |
| } | |
| @keyframes spinRotate { | |
| to { transform: rotate(360deg); } | |
| } | |
| @keyframes dashMove { | |
| to { stroke-dashoffset: -15; } | |
| } | |
| @keyframes drawCheck { | |
| to { stroke-dashoffset: 0; } | |
| } | |
| @keyframes slideUpFade { | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="bg-grid"></div> | |
| <div class="ambient-light"></div> | |
| <div class="app-container"> | |
| <div class="login-card"> | |
| <div class="logo-wrapper"> | |
| <div class="logo-core"> | |
| <svg class="logo-svg" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| <defs> | |
| <linearGradient id="primaryGrad" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" stop-color="#FFA600" /> | |
| <stop offset="100%" stop-color="#FF6B00" /> | |
| </linearGradient> | |
| </defs> | |
| <g class="outer-hex"> | |
| <path d="M50 2L91.569 26V74L50 98L8.431 74V26L50 2Z" stroke="url(#primaryGrad)" stroke-width="2" stroke-opacity="0.3"/> | |
| <path d="M50 8L86.373 29V71L50 92L13.627 71V29L50 8Z" stroke="url(#primaryGrad)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" class="inner-dash"/> | |
| </g> | |
| <path d="M50 22L70 34V66L50 78L30 66V34L50 22Z" fill="rgba(255, 107, 0, 0.1)" stroke="url(#primaryGrad)" stroke-width="2"/> | |
| <path d="M45 40L55 50L45 60" stroke="url(#primaryGrad)" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> | |
| </svg> | |
| </div> | |
| </div> | |
| <div class="header-section"> | |
| <h1>Secure Terminal Access</h1> | |
| <p>Please authenticate to continue</p> | |
| </div> | |
| <form id="terminalForm"> | |
| <div class="form-group"> | |
| <div class="input-icon"> | |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path> | |
| </svg> | |
| </div> | |
| <input type="password" id="secretKey" class="secure-input" placeholder="Enter Security Key" required autocomplete="off"> | |
| <button type="button" class="toggle-icon" id="togglePassword"> | |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="eyeIconSVG"> | |
| <path class="eye-path" d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path> | |
| <circle class="eye-circle" cx="12" cy="12" r="3"></circle> | |
| <line class="eye-slash" x1="1" y1="1" x2="23" y2="23" style="display:none;"></line> | |
| </svg> | |
| </button> | |
| </div> | |
| <button type="submit" class="btn-connect" id="connectBtn"> | |
| <span class="btn-text" id="btnText">Connect to Terminal</span> | |
| <div class="loader-ring"></div> | |
| </button> | |
| </form> | |
| <div class="auth-success" id="successScreen"> | |
| <svg class="shield-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path> | |
| <polyline points="9 12 11 14 15 10"></polyline> | |
| </svg> | |
| <div class="success-msg">ACCESS GRANTED</div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const form = document.getElementById('terminalForm'); | |
| const keyInput = document.getElementById('secretKey'); | |
| const toggleBtn = document.getElementById('togglePassword'); | |
| const eyeSlash = document.querySelector('.eye-slash'); | |
| const connectBtn = document.getElementById('connectBtn'); | |
| const btnText = document.getElementById('btnText'); | |
| const successScreen = document.getElementById('successScreen'); | |
| // Password Visibility Toggle Logic | |
| toggleBtn.addEventListener('click', () => { | |
| if (keyInput.type === 'password') { | |
| keyInput.type = 'text'; | |
| eyeSlash.style.display = 'block'; | |
| toggleBtn.style.color = 'var(--brand-orange)'; | |
| } else { | |
| keyInput.type = 'password'; | |
| eyeSlash.style.display = 'none'; | |
| toggleBtn.style.color = 'var(--text-secondary)'; | |
| } | |
| }); | |
| // Real Backend Form Submit Handling | |
| form.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const keyValue = keyInput.value.trim(); | |
| if(!keyValue) return; | |
| // Start Loading State | |
| connectBtn.classList.add('is-loading'); | |
| keyInput.disabled = true; | |
| try { | |
| // Send password to backend | |
| const response = await fetch('/login', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/x-www-form-urlencoded', | |
| }, | |
| body: `password=${encodeURIComponent(keyValue)}` | |
| }); | |
| connectBtn.classList.remove('is-loading'); | |
| // Check response status | |
| if (response.url.endsWith('/login?error=1')) { | |
| // Error State | |
| keyInput.disabled = false; | |
| keyInput.style.borderColor = '#ef4444'; | |
| btnText.textContent = "Invalid Key! Try Again"; | |
| btnText.style.color = '#ef4444'; | |
| setTimeout(() => { | |
| keyInput.style.borderColor = 'var(--border-color)'; | |
| btnText.textContent = "Connect to Terminal"; | |
| btnText.style.color = '#fff'; | |
| }, 2500); | |
| } else { | |
| // Success State | |
| successScreen.classList.add('active'); | |
| setTimeout(() => { | |
| window.location.href = '/'; | |
| }, 1500); | |
| } | |
| } catch (error) { | |
| console.error("Connection failed", error); | |
| connectBtn.classList.remove('is-loading'); | |
| keyInput.disabled = false; | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |