cron-itself / app.py
CORVO-AI's picture
Update app.py
29bfaa9 verified
Raw
History Blame Contribute Delete
4.96 kB
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.")
@app.route("/")
def index():
return jsonify({
"service": "Cron-like pinger",
"running": state["running"],
"total_urls": len(ALL_URLS),
"endpoints": ["/start", "/end", "/status", "/urls"],
})
@app.route("/start", methods=["GET", "POST"])
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,
})
@app.route("/end", methods=["GET", "POST"])
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"})
@app.route("/status")
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,
})
@app.route("/urls")
def urls():
return jsonify({"count": len(ALL_URLS), "urls": ALL_URLS})
@app.route("/results")
def results():
return jsonify(state["last_results"])
@app.route("/health")
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)