Spaces:
Runtime error
Runtime error
Upload api.py
Browse files
api.py
CHANGED
|
@@ -22,9 +22,11 @@ import imaplib
|
|
| 22 |
import email
|
| 23 |
from email.header import decode_header
|
| 24 |
import re
|
| 25 |
-
import hashlib
|
| 26 |
-
import time
|
| 27 |
-
import
|
|
|
|
|
|
|
| 28 |
import io
|
| 29 |
from urllib.parse import urljoin
|
| 30 |
from dotenv import load_dotenv
|
|
@@ -120,9 +122,25 @@ logger.addHandler(log_handler)
|
|
| 120 |
|
| 121 |
# --- 2. SEGURIDAD Y CIFRADO ---
|
| 122 |
# Se utiliza el módulo centralizado `crypto.py`
|
| 123 |
-
INTERNAL_API_TOKEN = os.getenv("INTERNAL_API_TOKEN", "default-dev-token")
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
if x_internal_token != INTERNAL_API_TOKEN:
|
| 127 |
raise HTTPException(status_code=403, detail="Acceso denegado: Token interno inválido.")
|
| 128 |
return x_internal_token
|
|
@@ -139,7 +157,76 @@ app.add_middleware(
|
|
| 139 |
allow_headers=["*"],
|
| 140 |
)
|
| 141 |
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
def estado():
|
| 144 |
return {"status": "Online", "engine": "Proyelec Core v6.1 — Token-Optimized"}
|
| 145 |
|
|
|
|
| 22 |
import email
|
| 23 |
from email.header import decode_header
|
| 24 |
import re
|
| 25 |
+
import hashlib
|
| 26 |
+
import time
|
| 27 |
+
import threading
|
| 28 |
+
from datetime import datetime
|
| 29 |
+
import pandas as pd
|
| 30 |
import io
|
| 31 |
from urllib.parse import urljoin
|
| 32 |
from dotenv import load_dotenv
|
|
|
|
| 122 |
|
| 123 |
# --- 2. SEGURIDAD Y CIFRADO ---
|
| 124 |
# Se utiliza el módulo centralizado `crypto.py`
|
| 125 |
+
INTERNAL_API_TOKEN = os.getenv("INTERNAL_API_TOKEN", "default-dev-token")
|
| 126 |
+
RADAR_AUTO_SCAN_ENABLED = os.getenv("RADAR_AUTO_SCAN_ENABLED", "true").strip().lower() in ["1", "true", "yes", "si", "sí", "on"]
|
| 127 |
+
RADAR_AUTO_SCAN_INTERVAL_MINUTES = max(5, int(os.getenv("RADAR_AUTO_SCAN_INTERVAL_MINUTES", "25") or 25))
|
| 128 |
+
RADAR_AUTO_SCAN_ON_STARTUP = os.getenv("RADAR_AUTO_SCAN_ON_STARTUP", "false").strip().lower() in ["1", "true", "yes", "si", "sí", "on"]
|
| 129 |
+
RADAR_SCHEDULER_STATE = {
|
| 130 |
+
"enabled": RADAR_AUTO_SCAN_ENABLED,
|
| 131 |
+
"interval_minutes": RADAR_AUTO_SCAN_INTERVAL_MINUTES,
|
| 132 |
+
"running": False,
|
| 133 |
+
"last_started": None,
|
| 134 |
+
"last_finished": None,
|
| 135 |
+
"last_result": None,
|
| 136 |
+
"last_error": None,
|
| 137 |
+
"next_run_at": None,
|
| 138 |
+
}
|
| 139 |
+
_radar_scheduler_stop = threading.Event()
|
| 140 |
+
_radar_scan_lock = threading.Lock()
|
| 141 |
+
_radar_scheduler_thread = None
|
| 142 |
+
|
| 143 |
+
def verify_internal_token(x_internal_token: str = Header(None)):
|
| 144 |
if x_internal_token != INTERNAL_API_TOKEN:
|
| 145 |
raise HTTPException(status_code=403, detail="Acceso denegado: Token interno inválido.")
|
| 146 |
return x_internal_token
|
|
|
|
| 157 |
allow_headers=["*"],
|
| 158 |
)
|
| 159 |
|
| 160 |
+
def run_radar_auto_scan(source="scheduler"):
|
| 161 |
+
"""Ejecuta el escaneo del Radar SLI de forma segura para jobs automáticos."""
|
| 162 |
+
if not _radar_scan_lock.acquire(blocking=False):
|
| 163 |
+
logger.info("[RADAR AUTO] Escaneo omitido: ya hay un escaneo en curso.")
|
| 164 |
+
return {"status": "skipped", "reason": "scan_already_running"}
|
| 165 |
+
|
| 166 |
+
RADAR_SCHEDULER_STATE["running"] = True
|
| 167 |
+
RADAR_SCHEDULER_STATE["last_started"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 168 |
+
RADAR_SCHEDULER_STATE["last_error"] = None
|
| 169 |
+
|
| 170 |
+
try:
|
| 171 |
+
import sli_scraper
|
| 172 |
+
logger.info(f"[RADAR AUTO] Iniciando escaneo SLI ({source}).")
|
| 173 |
+
result = sli_scraper.ejecutar_radar_detallado(db_module=db)
|
| 174 |
+
RADAR_SCHEDULER_STATE["last_result"] = result
|
| 175 |
+
RADAR_SCHEDULER_STATE["last_finished"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 176 |
+
logger.info(f"[RADAR AUTO] Escaneo completado: {result}")
|
| 177 |
+
return {"status": "success", "result": result}
|
| 178 |
+
except Exception as exc:
|
| 179 |
+
error_text = str(exc)
|
| 180 |
+
RADAR_SCHEDULER_STATE["last_error"] = error_text
|
| 181 |
+
RADAR_SCHEDULER_STATE["last_finished"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 182 |
+
try:
|
| 183 |
+
db.registrar_escaneo_radar(0, 0, f"Auto radar error: {error_text}")
|
| 184 |
+
except Exception:
|
| 185 |
+
logger.exception("[RADAR AUTO] No se pudo registrar error de escaneo.")
|
| 186 |
+
logger.exception(f"[RADAR AUTO] Error en escaneo: {error_text}")
|
| 187 |
+
return {"status": "error", "error": error_text}
|
| 188 |
+
finally:
|
| 189 |
+
RADAR_SCHEDULER_STATE["running"] = False
|
| 190 |
+
_radar_scan_lock.release()
|
| 191 |
+
|
| 192 |
+
def radar_scheduler_loop():
|
| 193 |
+
interval_seconds = RADAR_AUTO_SCAN_INTERVAL_MINUTES * 60
|
| 194 |
+
if RADAR_AUTO_SCAN_ON_STARTUP:
|
| 195 |
+
run_radar_auto_scan(source="startup")
|
| 196 |
+
while not _radar_scheduler_stop.is_set():
|
| 197 |
+
next_run_ts = time.time() + interval_seconds
|
| 198 |
+
RADAR_SCHEDULER_STATE["next_run_at"] = datetime.fromtimestamp(next_run_ts).strftime("%Y-%m-%d %H:%M:%S")
|
| 199 |
+
if _radar_scheduler_stop.wait(interval_seconds):
|
| 200 |
+
break
|
| 201 |
+
run_radar_auto_scan()
|
| 202 |
+
|
| 203 |
+
@app.on_event("startup")
|
| 204 |
+
def start_radar_scheduler():
|
| 205 |
+
global _radar_scheduler_thread
|
| 206 |
+
if not RADAR_AUTO_SCAN_ENABLED:
|
| 207 |
+
logger.info("[RADAR AUTO] Scheduler desactivado por variable de entorno.")
|
| 208 |
+
return
|
| 209 |
+
if _radar_scheduler_thread and _radar_scheduler_thread.is_alive():
|
| 210 |
+
return
|
| 211 |
+
_radar_scheduler_stop.clear()
|
| 212 |
+
_radar_scheduler_thread = threading.Thread(target=radar_scheduler_loop, name="radar-sli-scheduler", daemon=True)
|
| 213 |
+
_radar_scheduler_thread.start()
|
| 214 |
+
logger.info(f"[RADAR AUTO] Scheduler iniciado cada {RADAR_AUTO_SCAN_INTERVAL_MINUTES} minutos.")
|
| 215 |
+
|
| 216 |
+
@app.on_event("shutdown")
|
| 217 |
+
def stop_radar_scheduler():
|
| 218 |
+
_radar_scheduler_stop.set()
|
| 219 |
+
logger.info("[RADAR AUTO] Scheduler detenido.")
|
| 220 |
+
|
| 221 |
+
@app.get("/api/v1/radar/scheduler")
|
| 222 |
+
def radar_scheduler_status(_token: str = Depends(verify_internal_token)):
|
| 223 |
+
return RADAR_SCHEDULER_STATE
|
| 224 |
+
|
| 225 |
+
@app.post("/api/v1/radar/scan-now")
|
| 226 |
+
def radar_scan_now(_token: str = Depends(verify_internal_token)):
|
| 227 |
+
return run_radar_auto_scan(source="manual_api")
|
| 228 |
+
|
| 229 |
+
@app.get("/")
|
| 230 |
def estado():
|
| 231 |
return {"status": "Online", "engine": "Proyelec Core v6.1 — Token-Optimized"}
|
| 232 |
|