Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI-Powered Daily Horoscopes</title> | |
| <link href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css" rel="stylesheet"> | |
| <style> | |
| .horoscope-card { | |
| background: var(--bs-dark); | |
| border: 1px solid var(--bs-secondary); | |
| border-radius: 8px; | |
| padding: 1.5rem; | |
| margin-bottom: 1rem; | |
| } | |
| .sign-badge { | |
| background: var(--bs-primary); | |
| color: white; | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 15px; | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| } | |
| .prediction-text { | |
| line-height: 1.6; | |
| margin-top: 1rem; | |
| } | |
| .loading { | |
| display: none; | |
| } | |
| .source-info { | |
| font-size: 0.875rem; | |
| color: var(--bs-secondary); | |
| margin-top: 0.5rem; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container mt-4"> | |
| <div class="row"> | |
| <div class="col-12"> | |
| <h1 class="text-center mb-4">AI-Powered Daily Horoscopes</h1> | |
| <p class="text-center text-muted mb-5">Get personalized horoscope predictions powered by AI consolidation from multiple trusted sources</p> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div class="col-md-6"> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h5>Single Sign Horoscope</h5> | |
| </div> | |
| <div class="card-body"> | |
| <form id="singleSignForm"> | |
| <div class="mb-3"> | |
| <label for="zodiacSign" class="form-label">Zodiac Sign</label> | |
| <select class="form-select" id="zodiacSign" required> | |
| <option value="">Select your sign</option> | |
| <option value="aries">Aries</option> | |
| <option value="taurus">Taurus</option> | |
| <option value="gemini">Gemini</option> | |
| <option value="cancer">Cancer</option> | |
| <option value="leo">Leo</option> | |
| <option value="virgo">Virgo</option> | |
| <option value="libra">Libra</option> | |
| <option value="scorpio">Scorpio</option> | |
| <option value="sagittarius">Sagittarius</option> | |
| <option value="capricorn">Capricorn</option> | |
| <option value="aquarius">Aquarius</option> | |
| <option value="pisces">Pisces</option> | |
| </select> | |
| </div> | |
| <div class="mb-3"> | |
| <label for="source" class="form-label">Source</label> | |
| <select class="form-select" id="source"> | |
| <option value="astrology.com">Astrology.com</option> | |
| <option value="horoscope.com">Horoscope.com</option> | |
| </select> | |
| </div> | |
| <button type="submit" class="btn btn-primary">Get Horoscope</button> | |
| <div class="loading mt-2"> | |
| <div class="spinner-border spinner-border-sm" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| <span class="ms-2">Fetching horoscope...</span> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="col-md-6"> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h5>All Signs with AI Consolidation</h5> | |
| </div> | |
| <div class="card-body"> | |
| <p class="text-muted">Scrape horoscopes for all zodiac signs from multiple sources and get AI-consolidated predictions.</p> | |
| <button type="button" class="btn btn-success" id="scrapeAllBtn">Scrape All Signs</button> | |
| <div class="loading mt-2" id="allSignsLoading"> | |
| <div class="spinner-border spinner-border-sm" role="status"> | |
| <span class="visually-hidden">Loading...</span> | |
| </div> | |
| <span class="ms-2">Scraping all horoscopes...</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row mt-4"> | |
| <div class="col-12"> | |
| <div id="results"></div> | |
| </div> | |
| </div> | |
| <div class="row mt-5"> | |
| <div class="col-12"> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h5>API Endpoints</h5> | |
| </div> | |
| <div class="card-body"> | |
| <h6>Available Endpoints:</h6> | |
| <ul class="list-group list-group-flush"> | |
| <li class="list-group-item"><strong>GET /api/health</strong> - Health check</li> | |
| <li class="list-group-item"><strong>POST /api/horoscope/scrape</strong> - Scrape single horoscope</li> | |
| <li class="list-group-item"><strong>POST /api/horoscope/scrape-all</strong> - Scrape all horoscopes</li> | |
| <li class="list-group-item"><strong>POST /api/horoscope/consolidate</strong> - AI consolidate predictions</li> | |
| <li class="list-group-item"><strong>GET /api/signs</strong> - Get zodiac signs list</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Single sign form handler | |
| document.getElementById('singleSignForm').addEventListener('submit', async function(e) { | |
| e.preventDefault(); | |
| const sign = document.getElementById('zodiacSign').value; | |
| const source = document.getElementById('source').value; | |
| const loading = document.querySelector('#singleSignForm .loading'); | |
| const submitBtn = document.querySelector('#singleSignForm button[type="submit"]'); | |
| if (!sign) { | |
| alert('Please select a zodiac sign'); | |
| return; | |
| } | |
| // Show loading | |
| loading.style.display = 'block'; | |
| submitBtn.disabled = true; | |
| try { | |
| const response = await fetch('/api/horoscope/scrape', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| sign: sign, | |
| source: source | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (data.success) { | |
| displaySingleResult(data); | |
| } else { | |
| displayError('Error: ' + (data.error || 'Unknown error occurred')); | |
| } | |
| } catch (error) { | |
| displayError('Network error: ' + error.message); | |
| } finally { | |
| // Hide loading | |
| loading.style.display = 'none'; | |
| submitBtn.disabled = false; | |
| } | |
| }); | |
| // Scrape all button handler | |
| document.getElementById('scrapeAllBtn').addEventListener('click', async function() { | |
| const loading = document.getElementById('allSignsLoading'); | |
| const btn = document.getElementById('scrapeAllBtn'); | |
| // Show loading | |
| loading.style.display = 'block'; | |
| btn.disabled = true; | |
| try { | |
| const response = await fetch('/api/horoscope/scrape-all', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({}) | |
| }); | |
| const data = await response.json(); | |
| if (data.success) { | |
| displayAllResults(data.results); | |
| } else { | |
| displayError('Error: ' + (data.error || 'Unknown error occurred')); | |
| } | |
| } catch (error) { | |
| displayError('Network error: ' + error.message); | |
| } finally { | |
| // Hide loading | |
| loading.style.display = 'none'; | |
| btn.disabled = false; | |
| } | |
| }); | |
| function displaySingleResult(data) { | |
| const resultsDiv = document.getElementById('results'); | |
| resultsDiv.innerHTML = ` | |
| <div class="horoscope-card"> | |
| <div class="d-flex justify-content-between align-items-center mb-3"> | |
| <span class="sign-badge">${data.sign.toUpperCase()}</span> | |
| <small class="source-info">Source: ${data.source}</small> | |
| </div> | |
| <div class="prediction-text">${data.prediction}</div> | |
| <div class="source-info"> | |
| Date: ${data.date} | Scraped: ${new Date(data.scraped_at).toLocaleString()} | |
| </div> | |
| </div> | |
| `; | |
| } | |
| function displayAllResults(results) { | |
| const resultsDiv = document.getElementById('results'); | |
| let html = '<h4 class="mb-4">All Horoscopes</h4>'; | |
| Object.keys(results).forEach(sign => { | |
| const signData = results[sign]; | |
| html += ` | |
| <div class="horoscope-card"> | |
| <div class="mb-3"> | |
| <span class="sign-badge">${sign.toUpperCase()}</span> | |
| </div> | |
| `; | |
| Object.keys(signData).forEach(source => { | |
| const data = signData[source]; | |
| if (data.success) { | |
| html += ` | |
| <div class="mb-3"> | |
| <strong>${source}:</strong> | |
| <div class="prediction-text">${data.prediction}</div> | |
| <div class="source-info">Date: ${data.date}</div> | |
| </div> | |
| `; | |
| } else { | |
| html += ` | |
| <div class="mb-3"> | |
| <strong>${source}:</strong> | |
| <div class="text-danger">Error: ${data.error}</div> | |
| </div> | |
| `; | |
| } | |
| }); | |
| html += '</div>'; | |
| }); | |
| resultsDiv.innerHTML = html; | |
| } | |
| function displayError(message) { | |
| const resultsDiv = document.getElementById('results'); | |
| resultsDiv.innerHTML = ` | |
| <div class="alert alert-danger" role="alert"> | |
| ${message} | |
| </div> | |
| `; | |
| } | |
| // Load health check on page load | |
| window.addEventListener('load', async function() { | |
| try { | |
| const response = await fetch('/api/health'); | |
| const data = await response.json(); | |
| console.log('API Health:', data); | |
| } catch (error) { | |
| console.error('Health check failed:', error); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |