Spaces:
Paused
Paused
| from flask import Flask, jsonify | |
| import threading | |
| import time | |
| import requests | |
| from datetime import datetime | |
| import logging | |
| # Setup logging | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| app = Flask(__name__) | |
| # ============ CONFIG ============ | |
| NUM_SERVERS = 55 # serverclass1 to serverclass55 | |
| PING_INTERVAL = 21600 # seconds (5 minutes) - like cron-job.org | |
| REQUEST_TIMEOUT = 30 | |
| # Extra fixed URLs | |
| EXTRA_URLS = [ | |
| "https://dooratre-backup.hf.space/health", | |
| "https://dooratre-reload-bot.hf.space/health", | |
| "https://corvo-ai-cron.hf.space/health", | |
| ] | |
| def build_urls(): | |
| urls = [] | |
| urls.extend(EXTRA_URLS) | |
| return urls | |
| ALL_URLS = build_urls() | |
| # ============ STATE ============ | |
| state = { | |
| "running": False, | |
| "thread": None, | |
| "lock": threading.Lock(), | |
| "stop_event": threading.Event(), | |
| "last_cycle_start": None, | |
| "last_cycle_end": None, | |
| "cycles_completed": 0, | |
| "total_pings": 0, | |
| "success_count": 0, | |
| "fail_count": 0, | |
| "last_results": {}, | |
| } | |
| def ping_url(url): | |
| try: | |
| r = requests.get(url, timeout=REQUEST_TIMEOUT) | |
| ok = r.status_code == 200 | |
| return ok, r.status_code, None | |
| except Exception as e: | |
| return False, None, str(e) | |
| def cron_worker(stop_event: threading.Event): | |
| logger.info(f"Cron worker started. Total URLs: {len(ALL_URLS)}") | |
| while not stop_event.is_set(): | |
| cycle_start = datetime.utcnow().isoformat() | |
| state["last_cycle_start"] = cycle_start | |
| logger.info(f"=== Cycle started at {cycle_start} ===") | |
| for url in ALL_URLS: | |
| if stop_event.is_set(): | |
| logger.info("Stop event received, breaking cycle.") | |
| break | |
| ok, status, err = ping_url(url) | |
| state["total_pings"] += 1 | |
| if ok: | |
| state["success_count"] += 1 | |
| logger.info(f"[OK {status}] {url}") | |
| else: | |
| state["fail_count"] += 1 | |
| logger.warning(f"[FAIL {status}] {url} | err={err}") | |
| state["last_results"][url] = { | |
| "ok": ok, | |
| "status": status, | |
| "error": err, | |
| "time": datetime.utcnow().isoformat(), | |
| } | |
| state["cycles_completed"] += 1 | |
| state["last_cycle_end"] = datetime.utcnow().isoformat() | |
| logger.info(f"=== Cycle done. Sleeping {PING_INTERVAL}s ===") | |
| # Sleep but be responsive to stop_event | |
| stop_event.wait(PING_INTERVAL) | |
| logger.info("Cron worker stopped.") | |
| def index(): | |
| return jsonify({ | |
| "service": "Cron-like pinger", | |
| "running": state["running"], | |
| "total_urls": len(ALL_URLS), | |
| "endpoints": ["/start", "/end", "/status", "/urls"], | |
| }) | |
| def start(): | |
| with state["lock"]: | |
| if state["running"]: | |
| return jsonify({"status": "already_running"}), 200 | |
| state["stop_event"] = threading.Event() | |
| t = threading.Thread(target=cron_worker, args=(state["stop_event"],), daemon=True) | |
| state["thread"] = t | |
| state["running"] = True | |
| t.start() | |
| logger.info("Cron started via /start") | |
| return jsonify({ | |
| "status": "started", | |
| "total_urls": len(ALL_URLS), | |
| "interval_seconds": PING_INTERVAL, | |
| }) | |
| def end(): | |
| with state["lock"]: | |
| if not state["running"]: | |
| return jsonify({"status": "not_running"}), 200 | |
| state["stop_event"].set() | |
| state["running"] = False | |
| logger.info("Cron stop requested via /end") | |
| return jsonify({"status": "stopping"}) | |
| def status(): | |
| return jsonify({ | |
| "running": state["running"], | |
| "total_urls": len(ALL_URLS), | |
| "cycles_completed": state["cycles_completed"], | |
| "total_pings": state["total_pings"], | |
| "success_count": state["success_count"], | |
| "fail_count": state["fail_count"], | |
| "last_cycle_start": state["last_cycle_start"], | |
| "last_cycle_end": state["last_cycle_end"], | |
| "interval_seconds": PING_INTERVAL, | |
| }) | |
| def urls(): | |
| return jsonify({"count": len(ALL_URLS), "urls": ALL_URLS}) | |
| def results(): | |
| return jsonify(state["last_results"]) | |
| def health(): | |
| return jsonify({"status": "ok"}) | |
| if __name__ == "__main__": | |
| # Auto-start cron on boot (optional - comment out if you only want manual /start) | |
| # Uncomment below if you want it to auto-run: | |
| # state["stop_event"] = threading.Event() | |
| # t = threading.Thread(target=cron_worker, args=(state["stop_event"],), daemon=True) | |
| # state["thread"] = t | |
| # state["running"] = True | |
| # t.start() | |
| app.run(host="0.0.0.0", port=7860, debug=False, threaded=True) |