| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Verification Required - TTS Arena</title> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> |
| <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" async defer></script> |
| <style> |
| :root { |
| --primary-color: #5046e5; |
| --secondary-color: #f0f0f0; |
| --text-color: #333; |
| --light-gray: #f5f5f5; |
| --border-color: #e0e0e0; |
| --shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| --radius: 8px; |
| } |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Inter', sans-serif; |
| } |
| |
| body { |
| color: var(--text-color); |
| display: flex; |
| min-height: 100vh; |
| justify-content: center; |
| align-items: center; |
| background-color: var(--light-gray); |
| } |
| |
| .verification-container { |
| background-color: white; |
| border-radius: var(--radius); |
| box-shadow: var(--shadow); |
| padding: 32px; |
| width: 100%; |
| max-width: 450px; |
| text-align: center; |
| } |
| |
| .logo { |
| font-size: 24px; |
| font-weight: 700; |
| margin-bottom: 24px; |
| color: var(--primary-color); |
| } |
| |
| h1 { |
| font-size: 20px; |
| margin-bottom: 16px; |
| color: var(--text-color); |
| } |
| |
| p { |
| margin-bottom: 24px; |
| color: #666; |
| line-height: 1.5; |
| } |
| |
| .turnstile-container { |
| display: flex; |
| justify-content: center; |
| margin-bottom: 24px; |
| } |
| |
| .btn { |
| background-color: var(--primary-color); |
| color: white; |
| border: none; |
| border-radius: var(--radius); |
| padding: 12px 24px; |
| font-weight: 500; |
| cursor: pointer; |
| font-size: 1rem; |
| transition: background-color 0.2s; |
| width: 100%; |
| } |
| |
| .btn:hover { |
| background-color: #4038c7; |
| } |
| |
| .btn:disabled { |
| background-color: #a8a4e0; |
| cursor: not-allowed; |
| } |
| |
| .loader { |
| display: none; |
| width: 24px; |
| height: 24px; |
| border: 3px solid rgba(255, 255, 255, 0.3); |
| border-radius: 50%; |
| border-top-color: white; |
| animation: spin 1s ease infinite; |
| margin: 0 auto; |
| } |
| |
| @keyframes spin { |
| to { |
| transform: rotate(360deg); |
| } |
| } |
| |
| .btn.loading .btn-text { |
| display: none; |
| font-size: 1rem; |
| } |
| |
| .btn.loading .loader { |
| display: inline-block; |
| } |
| |
| .status-message { |
| margin-top: 16px; |
| font-size: 14px; |
| color: #666; |
| display: none; |
| } |
| |
| |
| @media (prefers-color-scheme: dark) { |
| :root { |
| --primary-color: #6c63ff; |
| --secondary-color: #2d2b38; |
| --text-color: #e0e0e0; |
| --light-gray: #1e1e24; |
| --border-color: #3a3a45; |
| --shadow: 0 2px 8px rgba(0, 0, 0, 0.3); |
| } |
| |
| body { |
| background-color: #121218; |
| } |
| |
| .verification-container { |
| background-color: var(--light-gray); |
| border: 1px solid var(--border-color); |
| } |
| |
| p { |
| color: #aaa; |
| } |
| |
| .status-message { |
| color: #aaa; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="verification-container"> |
| <div class="logo">TTS Arena</div> |
| <h1>Verification Required</h1> |
| <p>Please complete the verification below to access TTS Arena.</p> |
| |
| <div id="turnstile-form"> |
| <div class="turnstile-container"> |
| <div id="cf-turnstile" class="cf-turnstile"></div> |
| </div> |
| <button type="button" class="btn" id="submit-btn" disabled onclick="submitVerification()"> |
| <span class="btn-text">Continue to TTS Arena</span> |
| <span class="loader"></span> |
| </button> |
| <div id="status-message" class="status-message"></div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let turnstileToken = ''; |
| const redirectUrl = '{{ redirect_url }}'; |
| |
| const secureRedirectUrl = redirectUrl.replace(/^http:\/\//i, 'https://'); |
| const verifyEndpoint = '{{ url_for("verify_turnstile") }}'; |
| const statusMessage = document.getElementById('status-message'); |
| const submitButton = document.getElementById('submit-btn'); |
| |
| |
| function onTurnstileSuccess(token) { |
| turnstileToken = token; |
| submitButton.disabled = false; |
| console.log("Turnstile verification successful"); |
| } |
| |
| |
| function onTurnstileError(error) { |
| console.error("Turnstile error:", error); |
| statusMessage.textContent = "Verification error. Please try again."; |
| statusMessage.style.display = "block"; |
| } |
| |
| |
| function submitVerification() { |
| if (!turnstileToken) { |
| statusMessage.textContent = "Please complete the verification first."; |
| statusMessage.style.display = "block"; |
| return; |
| } |
| |
| |
| submitButton.classList.add('loading'); |
| submitButton.disabled = true; |
| |
| |
| const formData = new FormData(); |
| formData.append('cf-turnstile-response', turnstileToken); |
| formData.append('redirect_url', secureRedirectUrl); |
| |
| |
| fetch(verifyEndpoint, { |
| method: 'POST', |
| body: formData, |
| credentials: 'same-origin', |
| headers: { |
| 'X-Requested-With': 'XMLHttpRequest', |
| 'Accept': 'application/json' |
| } |
| }) |
| .then(response => { |
| if (response.redirected) { |
| |
| window.location.href = response.url; |
| } else { |
| return response.json().then(data => { |
| if (data.success) { |
| |
| window.location.href = secureRedirectUrl; |
| } else { |
| throw new Error("Verification failed"); |
| } |
| }); |
| } |
| }) |
| .catch(error => { |
| console.error("Verification error:", error); |
| statusMessage.textContent = "Verification failed. Please try again."; |
| statusMessage.style.display = "block"; |
| submitButton.classList.remove('loading'); |
| submitButton.disabled = false; |
| |
| |
| if (typeof turnstile !== 'undefined') { |
| turnstile.reset('#cf-turnstile'); |
| } |
| }); |
| } |
| |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| function waitForTurnstile() { |
| if (typeof turnstile !== 'undefined') { |
| |
| turnstile.render('#cf-turnstile', { |
| sitekey: '{{ turnstile_site_key }}', |
| callback: onTurnstileSuccess, |
| 'error-callback': onTurnstileError |
| }); |
| } else { |
| |
| setTimeout(waitForTurnstile, 100); |
| } |
| } |
| |
| waitForTurnstile(); |
| }); |
| </script> |
| </body> |
| </html> |