""" routes/internal.py — Machine-callable endpoints for cron jobs. Protected by CRON_SECRET (set in .env). No session auth required. These routes are intentionally GET so they work with a plain `curl` call from a systemd timer or crontab without needing CSRF tokens. """ import hmac import logging import os from flask import Blueprint, jsonify, request logger = logging.getLogger("routes.internal") internal_bp = Blueprint("internal", __name__, url_prefix="/internal") def _check_token() -> bool: """Return True if the request carries the correct CRON_SECRET.""" expected = os.environ.get("CRON_SECRET", "").strip() if not expected: logger.warning("CRON_SECRET not set — internal endpoints are disabled.") return False provided = request.args.get("token", "") return hmac.compare_digest(provided, expected) @internal_bp.route("/cron/shift-reminders") def cron_shift_reminders(): """ Called by a systemd timer (or any scheduler) every few minutes. Sends reminder emails to staff with incomplete checks 40 min before shift end. Each user+shift pair receives at most one reminder per calendar day. Usage: curl "https://your-server/internal/cron/shift-reminders?token=" """ if not _check_token(): return jsonify({"error": "unauthorized"}), 401 from routes.admin_shifts import do_send_incomplete_reminders try: result = do_send_incomplete_reminders(triggered_by_user_id=None) logger.info( f"cron/shift-reminders: sent={result['sent']} skipped={result['skipped']} " f"total={result['total']}" ) return jsonify(result) except Exception as e: logger.error(f"cron/shift-reminders error: {e}") return jsonify({"error": str(e)}), 500