Download static/login.html from Arkm20/authapi: direct link, hf CLI and curl.
- Browser
- Download file 13.2 kB
-
https://huggingface.co/spaces/Arkm20/authapi/resolve/main/static/login.html
- Command line
-
hf download hf://spaces/Arkm20/authapi/static/login.html
-
curl -L -o login.html https://huggingface.co/spaces/Arkm20/authapi/resolve/main/static/login.html
13.2 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Login / Signup</title> | |
| <style> | |
| :root { | |
| --accent-color: #FF9500; | |
| --background-dark: #121212; | |
| --form-bg-color: rgba(30, 30, 30, 0.65); | |
| --input-bg-color: rgba(50, 50, 50, 0.7); | |
| --text-primary: #FFFFFF; | |
| --text-secondary: #AAAAAA; | |
| --border-color: rgba(255, 255, 255, 0.1); | |
| --error-color: #FF4747; | |
| --success-color: #34C759; | |
| } | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| background: var(--background-dark); | |
| /* Adding a subtle gradient for a more dynamic background */ | |
| background-image: radial-gradient(circle at 10% 20%, rgba(255, 149, 0, 0.15), transparent 50%), | |
| radial-gradient(circle at 80% 90%, rgba(255, 149, 0, 0.1), transparent 40%); | |
| } | |
| .form-container { | |
| width: 100%; | |
| max-width: 400px; | |
| margin: 20px; | |
| background: var(--form-bg-color); | |
| border-radius: 20px; | |
| border: 1px solid var(--border-color); | |
| box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); | |
| backdrop-filter: blur(15px); | |
| -webkit-backdrop-filter: blur(15px); | |
| overflow: hidden; | |
| transition: height 0.4s ease-in-out; | |
| } | |
| .form-flipper { | |
| position: relative; | |
| width: 100%; | |
| transition: transform 0.6s ease-in-out; | |
| transform-style: preserve-3d; | |
| } | |
| .form-wrapper { | |
| width: 100%; | |
| padding: 2.5rem; | |
| backface-visibility: hidden; | |
| -webkit-backface-visibility: hidden; /* for Safari */ | |
| transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out; | |
| } | |
| #loginFormWrapper { | |
| /* Starts visible */ | |
| } | |
| #signupFormWrapper { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| opacity: 0; | |
| transform: translateY(20px); | |
| pointer-events: none; /* Inactive form should not be interactive */ | |
| } | |
| /* State classes for switching */ | |
| .form-container.show-signup #loginFormWrapper { | |
| opacity: 0; | |
| transform: translateY(-20px); | |
| pointer-events: none; | |
| } | |
| .form-container.show-signup #signupFormWrapper { | |
| opacity: 1; | |
| transform: translateY(0); | |
| pointer-events: all; | |
| } | |
| h2 { | |
| text-align: center; | |
| color: var(--text-primary); | |
| font-size: 2rem; | |
| margin-bottom: 0.5rem; | |
| font-weight: 600; | |
| } | |
| .form-subtitle { | |
| text-align: center; | |
| color: var(--accent-color); | |
| margin-bottom: 2rem; | |
| font-weight: 500; | |
| } | |
| .form-group { | |
| margin-bottom: 1.5rem; | |
| position: relative; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: .5rem; | |
| color: var(--text-secondary); | |
| font-size: 0.9rem; | |
| font-weight: 500; | |
| } | |
| input { | |
| width: 100%; | |
| padding: 0.85rem 1rem; | |
| box-sizing: border-box; | |
| border: 1px solid transparent; | |
| border-radius: 10px; | |
| background-color: var(--input-bg-color); | |
| color: var(--text-primary); | |
| font-size: 1rem; | |
| transition: border-color 0.3s, box-shadow 0.3s; | |
| } | |
| input:focus { | |
| outline: none; | |
| border-color: var(--accent-color); | |
| box-shadow: 0 0 0 3px rgba(255, 149, 0, 0.3); | |
| } | |
| button { | |
| width: 100%; | |
| padding: 0.9rem; | |
| border: none; | |
| border-radius: 10px; | |
| color: var(--text-primary); | |
| background-color: var(--accent-color); | |
| cursor: pointer; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| transition: transform 0.2s, background-color 0.2s; | |
| margin-top: 1rem; | |
| } | |
| button:hover { | |
| transform: translateY(-2px); | |
| background-color: #ffae42; /* A slightly lighter shade for hover */ | |
| } | |
| button:disabled { | |
| opacity: 0.5; | |
| cursor: not-allowed; | |
| transform: translateY(0); | |
| } | |
| .switch-form-btn { | |
| background: none; | |
| border: none; | |
| color: var(--accent-color); | |
| cursor: pointer; | |
| font-size: 0.9rem; | |
| font-weight: 500; | |
| padding: 0; | |
| margin-top: 1.5rem; | |
| text-align: center; | |
| width: 100%; | |
| display: block; | |
| } | |
| .switch-form-btn:hover { | |
| text-decoration: underline; | |
| transform: none; /* Override button hover */ | |
| background: none; | |
| } | |
| #message { | |
| margin-top: 1rem; | |
| text-align: center; | |
| font-weight: 500; | |
| font-size: 0.9rem; | |
| height: 20px; /* Reserve space to prevent layout shift */ | |
| transition: color 0.3s; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="form-container" id="formContainer"> | |
| <div id="loginFormWrapper" class="form-wrapper"> | |
| <form id="loginForm"> | |
| <h2>Animex</h2> | |
| <p class="form-subtitle">Welcome Back!</p> | |
| <div class="form-group"> | |
| <label for="login-username">Username</label> | |
| <input type="text" id="login-username" name="username" required autocomplete="username"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="login-password">Password</label> | |
| <input type="password" id="login-password" name="password" required autocomplete="current-password"> | |
| </div> | |
| <button type="submit" id="loginBtn">Login</button> | |
| </form> | |
| <button class="switch-form-btn" id="showSignup">Don't have an account? Sign Up</button> | |
| </div> | |
| <div id="signupFormWrapper" class="form-wrapper"> | |
| <form id="signupForm"> | |
| <h2>Animex</h2> | |
| <p class="form-subtitle">Get started with a new account</p> | |
| <div class="form-group"> | |
| <label for="signup-username">Username</label> | |
| <input type="text" id="signup-username" name="username" required autocomplete="username"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="signup-password">Password</label> | |
| <input type="password" id="signup-password" name="password" required autocomplete="new-password"> | |
| </div> | |
| <button type="submit" id="signupBtn">Sign Up</button> | |
| </form> | |
| <button class="switch-form-btn" id="showLogin">Already have an account? Login</button> | |
| </div> | |
| <p id="message"></p> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const API_BASE_URL = ''; | |
| const formContainer = document.getElementById('formContainer'); | |
| const loginFormWrapper = document.getElementById('loginFormWrapper'); | |
| const signupFormWrapper = document.getElementById('signupFormWrapper'); | |
| const loginForm = document.getElementById('loginForm'); | |
| const signupForm = document.getElementById('signupForm'); | |
| const loginBtn = document.getElementById('loginBtn'); | |
| const signupBtn = document.getElementById('signupBtn'); | |
| const showSignupBtn = document.getElementById('showSignup'); | |
| const showLoginBtn = document.getElementById('showLogin'); | |
| const messageEl = document.getElementById('message'); | |
| // Function to adjust container height based on content | |
| const adjustContainerHeight = () => { | |
| // Use a timeout to allow CSS transitions to start | |
| setTimeout(() => { | |
| const activeFormWrapper = formContainer.classList.contains('show-signup') | |
| ? signupFormWrapper | |
| : loginFormWrapper; | |
| formContainer.style.height = `${activeFormWrapper.scrollHeight + 70}px`; | |
| }, 100); | |
| }; | |
| // Initial height adjustment | |
| adjustContainerHeight(); | |
| // --- UI SWITCHING LOGIC --- | |
| showSignupBtn.addEventListener('click', (e) => { | |
| e.preventDefault(); | |
| formContainer.classList.add('show-signup'); | |
| messageEl.textContent = ''; // Clear message on switch | |
| adjustContainerHeight(); | |
| }); | |
| showLoginBtn.addEventListener('click', (e) => { | |
| e.preventDefault(); | |
| formContainer.classList.remove('show-signup'); | |
| messageEl.textContent = ''; // Clear message on switch | |
| adjustContainerHeight(); | |
| }); | |
| // --- API LOGIC --- | |
| const showMessage = (message, isError = true) => { | |
| messageEl.textContent = message; | |
| messageEl.style.color = isError ? 'var(--error-color)' : 'var(--success-color)'; | |
| } | |
| // --- LOGIN LOGIC --- | |
| loginForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| showMessage(''); | |
| loginBtn.disabled = true; | |
| loginBtn.textContent = 'Logging in...'; | |
| const formData = new FormData(); | |
| formData.append('username', e.target.username.value); | |
| formData.append('password', e.target.password.value); | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/token`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| const token = data.access_token; | |
| localStorage.setItem('accessToken', token); | |
| if (window.parent) { | |
| window.parent.postMessage({ accessToken: token }, '*'); | |
| } | |
| showMessage('Login Successful!', false); | |
| } else { | |
| showMessage(data.detail || 'Login failed.'); | |
| } | |
| } catch (error) { | |
| console.error('Login fetch error:', error); | |
| showMessage('An error occurred. Please try again.'); | |
| } finally { | |
| loginBtn.disabled = false; | |
| loginBtn.textContent = 'Login'; | |
| } | |
| }); | |
| // --- SIGNUP LOGIC --- | |
| signupForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| showMessage(''); | |
| signupBtn.disabled = true; | |
| signupBtn.textContent = 'Signing up...'; | |
| const username = e.target.username.value; | |
| const password = e.target.password.value; | |
| if (!username || !password) { | |
| showMessage('Please enter a username and password.'); | |
| signupBtn.disabled = false; | |
| signupBtn.textContent = 'Sign Up'; | |
| return; | |
| } | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/signup`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ username, password }) | |
| }); | |
| if (response.status === 201) { | |
| showMessage('Signup successful! Please log in.', false); | |
| // Automatically switch to login form | |
| setTimeout(() => { | |
| formContainer.classList.remove('show-signup'); | |
| adjustContainerHeight(); | |
| // Pre-fill username for convenience | |
| loginForm.username.value = username; | |
| loginForm.password.focus(); | |
| }, 1500); | |
| } else { | |
| const errorData = await response.json(); | |
| showMessage(errorData.detail || 'Signup failed.'); | |
| } | |
| } catch (error) { | |
| console.error('Signup error:', error); | |
| showMessage('An error occurred. Please try again.'); | |
| } finally { | |
| signupBtn.disabled = false; | |
| signupBtn.textContent = 'Sign Up'; | |
| } | |
| }); | |
| // Adjust height on window resize | |
| window.addEventListener('resize', adjustContainerHeight); | |
| }); | |
| </script> | |
| </body> | |
| </html> |