Download static/admin-login.html from harii66/gate: direct link, hf CLI and curl.
- Browser
- Download file 17.3 kB
-
https://huggingface.co/spaces/harii66/gate/resolve/main/static/admin-login.html
- Command line
-
hf download hf://spaces/harii66/gate/static/admin-login.html
-
curl -L -o admin-login.html https://huggingface.co/spaces/harii66/gate/resolve/main/static/admin-login.html
17.3 kB
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>管理员登录 - Media Gateway</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| background-size: 200% 200%; | |
| animation: gradientShift 15s ease infinite; | |
| min-height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px; | |
| } | |
| @keyframes gradientShift { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| @keyframes slideIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-30px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| @keyframes shake { | |
| 0%, 100% { transform: translateX(0); } | |
| 25% { transform: translateX(-10px); } | |
| 75% { transform: translateX(10px); } | |
| } | |
| .login-container { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(20px); | |
| border-radius: 30px; | |
| box-shadow: 0 30px 80px rgba(0, 0, 0, 0.3); | |
| overflow: hidden; | |
| max-width: 480px; | |
| width: 100%; | |
| animation: slideIn 0.6s ease; | |
| } | |
| .login-header { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 50px 40px; | |
| text-align: center; | |
| } | |
| .login-header h1 { | |
| font-size: 2.5em; | |
| margin-bottom: 12px; | |
| font-weight: 800; | |
| } | |
| .login-header p { | |
| font-size: 1.1em; | |
| opacity: 0.95; | |
| } | |
| .login-body { | |
| padding: 40px; | |
| } | |
| .form-group { | |
| margin-bottom: 25px; | |
| } | |
| .form-group label { | |
| display: block; | |
| margin-bottom: 10px; | |
| font-weight: 600; | |
| color: #1e293b; | |
| font-size: 0.95em; | |
| } | |
| .input-wrapper { | |
| position: relative; | |
| } | |
| .form-group input { | |
| width: 100%; | |
| padding: 16px 20px; | |
| border: 2px solid #e2e8f0; | |
| border-radius: 12px; | |
| font-size: 1em; | |
| transition: all 0.3s ease; | |
| background: white; | |
| font-family: inherit; | |
| } | |
| .form-group input:focus { | |
| outline: none; | |
| border-color: #6366f1; | |
| box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1); | |
| } | |
| .password-toggle { | |
| position: absolute; | |
| right: 16px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| color: #64748b; | |
| font-size: 1.3em; | |
| padding: 6px; | |
| } | |
| .password-toggle:hover { | |
| color: #6366f1; | |
| } | |
| .btn-login { | |
| width: 100%; | |
| padding: 18px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border: none; | |
| border-radius: 12px; | |
| font-size: 1.1em; | |
| font-weight: 700; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .btn-login:hover:not(:disabled) { | |
| transform: translateY(-3px); | |
| box-shadow: 0 12px 35px rgba(99, 102, 241, 0.4); | |
| } | |
| .btn-login:disabled { | |
| opacity: 0.7; | |
| cursor: not-allowed; | |
| } | |
| .error-message { | |
| background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); | |
| color: #991b1b; | |
| padding: 16px; | |
| border-radius: 12px; | |
| margin-bottom: 20px; | |
| display: none; | |
| animation: shake 0.5s; | |
| border: 2px solid #fca5a5; | |
| } | |
| .error-message.show { | |
| display: block; | |
| } | |
| .success-message { | |
| background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%); | |
| color: #065f46; | |
| padding: 16px; | |
| border-radius: 12px; | |
| margin-bottom: 20px; | |
| display: none; | |
| border: 2px solid #6ee7b7; | |
| } | |
| .success-message.show { | |
| display: block; | |
| } | |
| .info-box { | |
| background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); | |
| color: #1e40af; | |
| padding: 16px; | |
| border-radius: 12px; | |
| margin-bottom: 25px; | |
| font-size: 0.9em; | |
| line-height: 1.6; | |
| border: 1px solid #93c5fd; | |
| } | |
| .info-box strong { | |
| display: block; | |
| margin-bottom: 8px; | |
| font-size: 1.05em; | |
| } | |
| .login-footer { | |
| padding: 25px 40px; | |
| background: rgba(248, 250, 252, 0.8); | |
| text-align: center; | |
| color: #64748b; | |
| font-size: 0.9em; | |
| border-top: 1px solid #e2e8f0; | |
| } | |
| .login-footer a { | |
| color: #6366f1; | |
| text-decoration: none; | |
| font-weight: 600; | |
| } | |
| .login-footer a:hover { | |
| text-decoration: underline; | |
| } | |
| @media (max-width: 480px) { | |
| .login-container { | |
| margin: 10px; | |
| } | |
| .login-header { | |
| padding: 40px 30px; | |
| } | |
| .login-header h1 { | |
| font-size: 2em; | |
| } | |
| .login-body { | |
| padding: 30px 25px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="login-container"> | |
| <div class="login-header"> | |
| <h1>🔐 管理员登录</h1> | |
| <p>Media Gateway Admin Panel</p> | |
| </div> | |
| <div class="login-body"> | |
| <div id="errorMessage" class="error-message"></div> | |
| <div id="successMessage" class="success-message"></div> | |
| <div class="info-box"> | |
| <strong>💡 管理员功能</strong> | |
| 登录后可创建和管理用户账号,配置系统设置 | |
| </div> | |
| <form id="loginForm"> | |
| <div class="form-group"> | |
| <label for="username">👤 管理员账号</label> | |
| <input | |
| type="text" | |
| id="username" | |
| placeholder="请输入管理员账号" | |
| required | |
| autocomplete="username" | |
| > | |
| </div> | |
| <div class="form-group"> | |
| <label for="password">🔒 管理员密码</label> | |
| <div class="input-wrapper"> | |
| <input | |
| type="password" | |
| id="password" | |
| placeholder="请输入管理员密码" | |
| required | |
| autocomplete="current-password" | |
| > | |
| <button type="button" class="password-toggle" id="togglePassword"> | |
| 👁️ | |
| </button> | |
| </div> | |
| </div> | |
| <button type="submit" class="btn-login" id="loginBtn"> | |
| 🚀 登录管理后台 | |
| </button> | |
| </form> | |
| </div> | |
| <div class="login-footer"> | |
| <a href="/">← 返回首页</a> | |
| </div> | |
| </div> | |
| <script> | |
| (function() { | |
| 'use strict'; | |
| const API = window.location.origin; | |
| function togglePassword() { | |
| const passwordInput = document.getElementById('password'); | |
| const toggleBtn = document.getElementById('togglePassword'); | |
| if (!passwordInput || !toggleBtn) return; | |
| if (passwordInput.type === 'password') { | |
| passwordInput.type = 'text'; | |
| toggleBtn.textContent = '🙈'; | |
| } else { | |
| passwordInput.type = 'password'; | |
| toggleBtn.textContent = '👁️'; | |
| } | |
| } | |
| function showError(message) { | |
| const errorEl = document.getElementById('errorMessage'); | |
| const successEl = document.getElementById('successMessage'); | |
| if (!errorEl) return; | |
| successEl.classList.remove('show'); | |
| errorEl.textContent = '❌ ' + message; | |
| errorEl.classList.add('show'); | |
| setTimeout(() => { | |
| errorEl.classList.remove('show'); | |
| }, 5000); | |
| } | |
| function showSuccess(message) { | |
| const successEl = document.getElementById('successMessage'); | |
| const errorEl = document.getElementById('errorMessage'); | |
| if (!successEl) return; | |
| errorEl.classList.remove('show'); | |
| successEl.textContent = '✅ ' + message; | |
| successEl.classList.add('show'); | |
| } | |
| async function sha256(message) { | |
| try { | |
| const msgBuffer = new TextEncoder().encode(message); | |
| const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); | |
| const hashArray = Array.from(new Uint8Array(hashBuffer)); | |
| return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); | |
| } catch (error) { | |
| throw error; | |
| } | |
| } | |
| function clearAuth() { | |
| localStorage.removeItem('admin_token'); | |
| localStorage.removeItem('admin_token_expiry'); | |
| localStorage.removeItem('admin_username'); | |
| } | |
| function saveAuth(token, username) { | |
| const expiry = Date.now() + (24 * 60 * 60 * 1000); | |
| localStorage.setItem('admin_token', token); | |
| localStorage.setItem('admin_token_expiry', expiry.toString()); | |
| localStorage.setItem('admin_username', username); | |
| } | |
| async function login(username, password) { | |
| try { | |
| const passwordHash = await sha256(password); | |
| const response = await fetch(`${API}/api/admin/login`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| username: username, | |
| password_hash: passwordHash | |
| }) | |
| }); | |
| if (!response.ok) { | |
| const error = await response.json(); | |
| throw new Error(error.message || '登录失败'); | |
| } | |
| const data = await response.json(); | |
| if (data.success && data.token) { | |
| saveAuth(data.token, username); | |
| return true; | |
| } else { | |
| throw new Error('登录失败:未返回有效 token'); | |
| } | |
| } catch (error) { | |
| throw error; | |
| } | |
| } | |
| async function checkAuthStatus() { | |
| const token = localStorage.getItem('admin_token'); | |
| const expiry = localStorage.getItem('admin_token_expiry'); | |
| if (!token || !expiry) { | |
| return false; | |
| } | |
| if (Date.now() > parseInt(expiry)) { | |
| clearAuth(); | |
| return false; | |
| } | |
| try { | |
| const response = await fetch(`${API}/api/admin/check`, { | |
| headers: { | |
| 'Authorization': `Bearer ${token}` | |
| } | |
| }); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| if (data.authenticated) { | |
| return true; | |
| } | |
| } | |
| clearAuth(); | |
| return false; | |
| } catch (error) { | |
| clearAuth(); | |
| return false; | |
| } | |
| } | |
| function initialize() { | |
| const togglePasswordBtn = document.getElementById('togglePassword'); | |
| if (togglePasswordBtn) { | |
| togglePasswordBtn.addEventListener('click', togglePassword); | |
| } | |
| const loginForm = document.getElementById('loginForm'); | |
| if (loginForm) { | |
| loginForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const usernameInput = document.getElementById('username'); | |
| const passwordInput = document.getElementById('password'); | |
| const loginBtn = document.getElementById('loginBtn'); | |
| if (!usernameInput || !passwordInput || !loginBtn) return; | |
| const username = usernameInput.value.trim(); | |
| const password = passwordInput.value; | |
| if (!username || !password) { | |
| showError('请输入用户名和密码'); | |
| return; | |
| } | |
| loginBtn.disabled = true; | |
| loginBtn.textContent = '验证中...'; | |
| try { | |
| const success = await login(username, password); | |
| if (success) { | |
| showSuccess('登录成功!正在跳转...'); | |
| setTimeout(() => { | |
| window.location.href = '/admin'; | |
| }, 1500); | |
| } else { | |
| showError('登录失败,请重试'); | |
| loginBtn.disabled = false; | |
| loginBtn.textContent = '🚀 登录管理后台'; | |
| passwordInput.value = ''; | |
| passwordInput.focus(); | |
| } | |
| } catch (error) { | |
| showError(error.message || '登录失败,请重试'); | |
| loginBtn.disabled = false; | |
| loginBtn.textContent = '🚀 登录管理后台'; | |
| passwordInput.value = ''; | |
| passwordInput.focus(); | |
| } | |
| }); | |
| } | |
| checkAuthStatus().then(isValid => { | |
| if (isValid) { | |
| window.location.href = '/admin'; | |
| } else { | |
| } | |
| }); | |
| setTimeout(() => { | |
| const usernameInput = document.getElementById('username'); | |
| if (usernameInput) { | |
| usernameInput.focus(); | |
| } | |
| }, 300); | |
| } | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', initialize); | |
| } else { | |
| initialize(); | |
| } | |
| })(); | |
| </script> | |
| </body> | |
| </html> |