import os import time from datetime import datetime, timezone, timedelta # Fallback base URL since 'request.host_url' is not available in background threads SERVER_BASE_URL = os.getenv("SERVER_BASE_URL", "https://smartwave.matthiasg.dev") def job_server_cve_audit(shodan_auditor, db): """Job 1: Audits Flask server public IP for CVEs and saves to MongoDB.""" print("[Cron] Starting Server CVE Audit...") audit_results = shodan_auditor.audit_server_vulnerabilities() db.cve_audit_results.insert_one({ "audit_results": audit_results, "timestamp": datetime.now(timezone.utc) }) print(f"[Cron] Server CVE Audit finished. Status: {audit_results.get('status')}") def job_client_ip_audit(shodan_auditor, db): """ Job 2: Fetches distinct client IPs logged in the last 24h and audits them. Deduplicates IPs and applies rate limits to protect Shodan API credits. """ print("[Cron] Starting Client IP Security Audit...") pipeline = [ {"$group": { "_id": "$client_ip", "microwave_ids": {"$addToSet": "$microwave_id"}, "last_seen": {"$max": "$received_at"} }} ] recent_clients = list(db.device_ips.aggregate(pipeline)) for entry in recent_clients: client_ip = entry["_id"] # Skip local/private IPs if not client_ip or client_ip in ["127.0.0.1", "localhost"] or client_ip.startswith(("10.", "192.168.", "172.16.")): continue profile = shodan_auditor.profile_client_ip(client_ip) # Upsert client profile in a separate collection db.client_audit_results.update_one( {"client_ip": client_ip}, { "$set": { "client_ip": client_ip, "microwave_ids": entry["microwave_ids"], "last_seen": entry["last_seen"], "profile": profile, "audited_at": datetime.now(timezone.utc) } }, upsert=True ) # Pause 1 second to respect Shodan API rate limits (1 req/sec) time.sleep(1) print(f"[Cron] Client IP Security Audit completed for {len(recent_clients)} IPs.") def job_request_telemetry(send_command_func): """Job 3: Triggers MQTT command to request telemetry from all microwaves.""" print("[Cron] Sending MQTT Telemetry Request to cmd/all...") telemetry_url = f"{SERVER_BASE_URL.rstrip('/')}/telemetry" cmd_payload = { "action": "request_telemetry", "endpoint": telemetry_url } try: send_command_func(topic="cmd/all", payload=cmd_payload) print("[Cron] Telemetry request successfully published.") except Exception as e: print(f"[Cron Error] Failed to publish MQTT command: {str(e)}")