From 2d538d6542e0520557f266811cff611598e5f894 Mon Sep 17 00:00:00 2001 From: Dome Date: Tue, 5 May 2026 20:47:44 +0200 Subject: [PATCH] Update app.py --- app.py | 64 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/app.py b/app.py index fdd505f..dd16c1a 100644 --- a/app.py +++ b/app.py @@ -5,6 +5,7 @@ import logging import threading from collections import defaultdict from datetime import datetime, timezone +from threading import Lock import requests from flask import Flask, request, Response @@ -62,14 +63,11 @@ log_level = logging.DEBUG if os.getenv("DEBUG", "false").lower() == "true" else logging.basicConfig( level=log_level, - format="%(message)s" + format="%(message)s" ) -# 🔇 HTTP Noise entfernen logging.getLogger("werkzeug").setLevel(logging.ERROR) logging.getLogger("urllib3").setLevel(logging.WARNING) - -# 🔇 Gunicorn Access Logs entfernen logging.getLogger("gunicorn.access").setLevel(logging.WARNING) logger = logging.getLogger("matrix-interceptor") @@ -83,7 +81,7 @@ def log_event(event: str, **kwargs): logger.info(f"{base} {details}") def debug_log(title, data): - if os.getenv("DEBUG", "false").lower() == "true": + if config.debug: logger.debug(f"{title}: {json.dumps(data, default=str)}") # ============================================================ @@ -104,9 +102,10 @@ if missing: KNOWN_EXTERNAL_USERS = {} RATE_LIMIT = defaultdict(list) METRICS = defaultdict(int) +METRICS_LOCK = Lock() CACHE_FILE = "/app/cache/known_users.json" -CACHE_DIRTY = True +CACHE_DIRTY = False # ============================================================ # CACHE HANDLING @@ -114,19 +113,20 @@ CACHE_DIRTY = True def load_cache(): global KNOWN_EXTERNAL_USERS + if os.path.exists(CACHE_FILE): try: with open(CACHE_FILE, "r") as f: KNOWN_EXTERNAL_USERS = json.load(f) logger.info(f"Loaded cache with {len(KNOWN_EXTERNAL_USERS)} users") - except: + except Exception as e: + logger.error(f"Cache load failed: {e}") KNOWN_EXTERNAL_USERS = {} else: - # 🔥 neu: Datei initial anlegen os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True) with open(CACHE_FILE, "w") as f: json.dump({}, f) - logger.info("Initialized empty cache file") + logger.info("Initialized empty cache") def save_cache(): global CACHE_DIRTY @@ -154,7 +154,7 @@ def periodic_cache_save(): def extract_domain(user_id): try: return user_id.split(":")[1].lower().rstrip(".") - except: + except Exception: return "unknown" def is_external(user_id): @@ -175,30 +175,32 @@ def remember_user(user_id): global CACHE_DIRTY KNOWN_EXTERNAL_USERS[user_id] = time.time() CACHE_DIRTY = True + save_cache() # 🔥 critical safety write def is_local_room(room_id): try: return room_id.split(":")[1] == config.local_domain - except: + except Exception: return False def get_role(user_id): return "admin" if user_id in config.admin_users else "user" # ============================================================ -# RATE LIMIT (IMPROVED) +# RATE LIMIT # ============================================================ -def is_rate_limited(domain, sender): - key = f"{domain}:{sender}" +def is_rate_limited(domain): now = time.time() - RATE_LIMIT[key] = [t for t in RATE_LIMIT[key] if now - t < 60] + RATE_LIMIT[domain] = [ + t for t in RATE_LIMIT[domain] if now - t < 60 + ] - if len(RATE_LIMIT[key]) >= config.rate_limit_per_minute: + if len(RATE_LIMIT[domain]) >= config.rate_limit_per_minute: return True - RATE_LIMIT[key].append(now) + RATE_LIMIT[domain].append(now) return False # ============================================================ @@ -280,8 +282,8 @@ def is_user_in_local_rooms(user_id): if user_id in members_res.json().get("joined", {}): return True - except: - return False + except Exception as e: + logger.error(f"Fallback check failed: {e}") return False @@ -348,7 +350,9 @@ def create_room(): ) if not allowed: - METRICS["create_room_blocked"] += 1 + with METRICS_LOCK: + METRICS["create_room_blocked"] += 1 + log_event( "create_room_blocked", actor=user_id, @@ -359,7 +363,9 @@ def create_room(): return Response(json.dumps({"errcode": "M_FORBIDDEN"}), status=403) if is_dm: - METRICS["create_room_allowed"] += 1 + with METRICS_LOCK: + METRICS["create_room_allowed"] += 1 + log_event( "create_room_allowed", actor=user_id, @@ -387,13 +393,12 @@ def invite(room_id, event_id): domain = extract_domain(sender) - if is_rate_limited(domain, sender): + if is_rate_limited(domain): return Response(status=429) if domain in config.domain_whitelist: remember_user(sender) - METRICS["invite_allowed"] += 1 - return forward_request(...) + return forward_request( "PUT", f"{config.tuwunel_url}/_matrix/federation/v2/invite/{room_id}/{event_id}", request.headers, @@ -406,11 +411,8 @@ def invite(room_id, event_id): if not is_known_user(sender): - # 🔥 fallback fix if is_user_in_local_rooms(sender): remember_user(sender) - METRICS["invite_allowed"] += 1 - return forward_request( "PUT", f"{config.tuwunel_url}/_matrix/federation/v2/invite/{room_id}/{event_id}", @@ -419,7 +421,9 @@ def invite(room_id, event_id): ) else: - METRICS["invite_blocked"] += 1 + with METRICS_LOCK: + METRICS["invite_blocked"] += 1 + log_event( "invite_blocked", actor=sender, @@ -429,7 +433,9 @@ def invite(room_id, event_id): return Response(status=403) remember_user(sender) - METRICS["invite_allowed"] += 1 + + with METRICS_LOCK: + METRICS["invite_allowed"] += 1 return forward_request( "PUT",