| | import threading |
| | import time |
| | import requests |
| | import os |
| | import datetime |
| | from flask import Flask, jsonify, request |
| | from wsgiref.simple_server import make_server |
| |
|
| | app = Flask(__name__) |
| |
|
| | |
| | URL_FILE = 'urls.txt' |
| | SELF_PING_URL = "https://alvin3y1-ping.hf.space/" |
| | INTERVAL_SECONDS = 4 * 3600 |
| |
|
| | |
| | monitoring_state = {} |
| | scan_state = { |
| | 'is_scanning': False, |
| | 'total': 0, |
| | 'completed': 0, |
| | 'last_scan_time': 'Never' |
| | } |
| | state_lock = threading.Lock() |
| |
|
| | |
| | |
| | HTML_TEMPLATE = """ |
| | <!DOCTYPE html> |
| | <html lang="en" class="dark"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Service Status</title> |
| | <script src="https://cdn.tailwindcss.com"></script> |
| | </head> |
| | <body class="bg-black text-gray-200 min-h-screen p-6 font-sans"> |
| | <div class="max-w-2xl mx-auto"> |
| | <div class="flex justify-between items-center mb-8 border-b border-gray-800 pb-4"> |
| | <h1 class="text-xl font-bold">System Status</h1> |
| | <button onclick="triggerScan()" id="scan-btn" class="bg-gray-800 hover:bg-gray-700 px-3 py-1 text-sm rounded transition">Run Scan</button> |
| | </div> |
| | |
| | <div id="progress" class="hidden mb-6"> |
| | <div class="w-full bg-gray-800 h-1 rounded-full overflow-hidden"> |
| | <div id="bar" class="bg-blue-500 h-full w-0 transition-all duration-300"></div> |
| | </div> |
| | </div> |
| | |
| | <div id="alerts" class="space-y-3"></div> |
| | </div> |
| | |
| | <script> |
| | async function fetchStatus() { |
| | const res = await fetch('/api/status'); |
| | const data = await res.json(); |
| | |
| | // Handle Progress |
| | const prog = document.getElementById('progress'); |
| | if (data.scan.is_scanning) { |
| | prog.classList.remove('hidden'); |
| | const percent = (data.scan.completed / data.scan.total) * 100; |
| | document.getElementById('bar').style.width = percent + '%'; |
| | } else { |
| | prog.classList.add('hidden'); |
| | } |
| | |
| | // Handle Down URLs |
| | const container = document.getElementById('alerts'); |
| | container.innerHTML = data.down_urls.length ? '' : '<p class="text-green-500 text-center py-10">All Systems Operational</p>'; |
| | |
| | data.down_urls.forEach(u => { |
| | container.innerHTML += ` |
| | <div class="bg-red-950/20 border border-red-900/50 p-4 rounded flex justify-between items-center"> |
| | <div> |
| | <div class="text-sm font-mono text-red-200">${u.url}</div> |
| | <div class="text-xs text-red-500 mt-1">${u.msg} (${u.code})</div> |
| | </div> |
| | <button onclick="retry('${u.url}')" class="text-xs bg-red-900 px-2 py-1 rounded">Retry</button> |
| | </div>`; |
| | }); |
| | } |
| | async function triggerScan() { |
| | await fetch('/api/scan', {method: 'POST'}); |
| | fetchStatus(); |
| | } |
| | async function retry(url) { |
| | await fetch('/api/retry', {method: 'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({url})}); |
| | fetchStatus(); |
| | } |
| | setInterval(fetchStatus, 2000); |
| | fetchStatus(); |
| | </script> |
| | </body> |
| | </html> |
| | """ |
| |
|
| | |
| | def get_all_urls(): |
| | urls = [SELF_PING_URL] |
| | if os.path.exists(URL_FILE): |
| | with open(URL_FILE, 'r') as f: |
| | urls.extend([l.strip() for l in f if l.strip()]) |
| | return list(set(urls)) |
| |
|
| | def check_single_url(url): |
| | timestamp = datetime.datetime.now().strftime("%H:%M:%S") |
| | try: |
| | r = requests.get(url, timeout=10) |
| | res = {'status': 'UP' if r.status_code < 400 else 'DOWN', 'code': r.status_code, 'time': timestamp, 'msg': 'OK' if r.status_code < 400 else 'Error'} |
| | except Exception as e: |
| | res = {'status': 'DOWN', 'code': 'ERR', 'time': timestamp, 'msg': str(e)[:20]} |
| | with state_lock: |
| | monitoring_state[url] = res |
| |
|
| | def perform_full_scan(): |
| | urls = get_all_urls() |
| | with state_lock: |
| | scan_state.update({'is_scanning': True, 'total': len(urls), 'completed': 0}) |
| | for url in urls: |
| | check_single_url(url) |
| | with state_lock: |
| | scan_state['completed'] += 1 |
| | with state_lock: |
| | scan_state.update({'is_scanning': False, 'last_scan_time': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}) |
| |
|
| | def background_worker(): |
| | while True: |
| | perform_full_scan() |
| | time.sleep(INTERVAL_SECONDS) |
| |
|
| | |
| | @app.route('/') |
| | def home(): return HTML_TEMPLATE |
| |
|
| | @app.route('/api/status') |
| | def get_status(): |
| | with state_lock: |
| | down = [{'url': u, **s} for u, s in monitoring_state.items() if s['status'] == 'DOWN'] |
| | return jsonify({'scan': scan_state, 'down_urls': down}) |
| |
|
| | @app.route('/api/scan', methods=['POST']) |
| | def scan(): |
| | threading.Thread(target=perform_full_scan, daemon=True).start() |
| | return jsonify({"status": "started"}) |
| |
|
| | @app.route('/api/retry', methods=['POST']) |
| | def retry(): |
| | threading.Thread(target=check_single_url, args=(request.json['url'],), daemon=True).start() |
| | return jsonify({"status": "retrying"}) |
| |
|
| | if __name__ == '__main__': |
| | threading.Thread(target=background_worker, daemon=True).start() |
| | print("Server starting on http://0.0.0.0:7860") |
| | make_server('0.0.0.0', 7860, app).serve_forever() |
| |
|