# app.py (v2.0 - Autark & Transparent) from flask import Flask, jsonify, request, render_template_string import subprocess import sys import os import logging import uuid import json from datetime import datetime from pyngrok import ngrok, conf from config import Config # --- Konfiguration --- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') app = Flask(__name__) # Verzeichnis für Statusdateien der laufenden Jobs STATUS_DIR = "job_status" os.makedirs(STATUS_DIR, exist_ok=True) # SCRIPT MAP (Passen Sie hier bei Bedarf die Skriptnamen an, z.B. auf brancheneinstufung2.py) SCRIPT_MAP = { "run_duplicate_check": {"script": "duplicate_checker.py", "args": []}, "run_reclassify_branches": {"script": "brancheneinstufung.py", "args": ["--mode", "reclassify_branches"]}, "run_predict_technicians": {"script": "brancheneinstufung.py", "args": ["--mode", "predict_technicians"]}, } def setup_ngrok(): """Konfiguriert und startet den ngrok-Tunnel.""" try: authtoken = os.environ.get("NGROK_AUTHTOKEN") if not authtoken: if os.path.exists("ngrok_authtoken.txt"): with open("ngrok_authtoken.txt", "r") as f: authtoken = f.read().strip() if not authtoken: logging.error("NGROK_AUTHTOKEN nicht gefunden. Tunnel kann nicht gestartet werden.") return None conf.get_default().auth_token = authtoken public_url = ngrok.connect(8080, "http") logging.info(f"!!! Ngrok-Tunnel gestartet: {public_url} !!!") logging.info("!!! Bitte diese URL im Google Apps Script eintragen. !!!") return public_url except Exception as e: logging.error(f"Fehler beim Starten von ngrok: {e}") sys.exit(1) @app.route('/run-script', methods=['POST']) def run_script(): """ Startet ein Skript als Hintergrundprozess und verfolgt es über eine Job-ID. Verbesserte Fehlerbehandlung und Logging. """ action = "unknown" try: data = request.get_json() if not data: raise ValueError("Keine JSON-Daten im Request gefunden.") action = data.get('action') if not action or action not in SCRIPT_MAP: logging.warning(f"Ungültige oder fehlende Aktion empfangen: {action}") return jsonify({"status": "error", "message": "Ungültige oder fehlende Aktion."}), 400 script_config = SCRIPT_MAP[action] script_name = script_config["script"] script_args = script_config["args"] script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), script_name) if not os.path.exists(script_path): logging.error(f"Skript '{script_path}' für Aktion '{action}' nicht gefunden.") return jsonify({"status": "error", "message": f"Server-Fehler: Skript '{script_name}' nicht gefunden."}), 500 logging.info(f"Aktion '{action}' empfangen. Starte Skript: '{script_name}'...") python_executable = sys.executable command = [python_executable, script_path] + script_args script_dir = os.path.dirname(os.path.abspath(__file__)) # Führe den Prozess aus und fange stdout/stderr für Debugging ab process = subprocess.Popen( command, cwd=script_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Wir warten nicht, aber wir können kurz prüfen, ob es sofort einen Fehler gab # Dies ist eine Vereinfachung. Für eine vollwertige Lösung bräuchten wir einen Job-Queue. # Aber für jetzt sollte das den Fehler aufdecken. logging.info(f"Prozess für Aktion '{action}' erfolgreich mit PID {process.pid} gestartet.") return jsonify({"status": "success", "message": f"Aktion '{action}' wurde erfolgreich auf dem Server gestartet."}), 200 except Exception as e: logging.error(f"Kritischer Fehler in der run_script-Route bei Aktion '{action}': {e}", exc_info=True) return jsonify({"status": "error", "message": f"Server-Fehler: {str(e)}"}), 500 @app.route('/get-status', methods=['GET']) def get_status(): """Liest alle Job-Status-Dateien und gibt den aktuellen Stand zurück.""" all_statuses = [] for filename in os.listdir(STATUS_DIR): if filename.endswith(".json"): try: with open(os.path.join(STATUS_DIR, filename), 'r') as f: status_data = json.load(f) status_data['job_id'] = filename.replace('.json', '') all_statuses.append(status_data) except Exception as e: logging.warning(f"Konnte Statusdatei {filename} nicht lesen: {e}") # Sortiere nach Startzeit, neuester Job zuerst all_statuses.sort(key=lambda x: x.get('start_time', ''), reverse=True) return jsonify(all_statuses) if __name__ == '__main__': setup_ngrok() app.run(host='0.0.0.0', port=8080)