From 6072d297a648e49db4e62f936ff575417bdcdb0d Mon Sep 17 00:00:00 2001 From: Dome Date: Tue, 5 May 2026 20:18:54 +0200 Subject: [PATCH] Update app.py --- app.py | 52 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/app.py b/app.py index e97ea5f..df0eadf 100644 --- a/app.py +++ b/app.py @@ -93,8 +93,14 @@ if missing: KNOWN_EXTERNAL_USERS = {} RATE_LIMIT = defaultdict(list) +METRICS = defaultdict(int) CACHE_FILE = "/app/cache/known_users.json" +CACHE_DIRTY = False + +# ============================================================ +# CACHE HANDLING +# ============================================================ def load_cache(): global KNOWN_EXTERNAL_USERS @@ -107,13 +113,24 @@ def load_cache(): KNOWN_EXTERNAL_USERS = {} def save_cache(): + global CACHE_DIRTY + + if not CACHE_DIRTY: + return + try: os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True) with open(CACHE_FILE, "w") as f: json.dump(KNOWN_EXTERNAL_USERS, f) + CACHE_DIRTY = False except Exception as e: logger.error(f"Failed to save cache: {e}") +def periodic_cache_save(): + while True: + save_cache() + time.sleep(30) + # ============================================================ # HELPERS # ============================================================ @@ -134,14 +151,14 @@ def is_known_user(user_id): if time.time() - ts > config.cache_ttl: del KNOWN_EXTERNAL_USERS[user_id] - save_cache() return False return True def remember_user(user_id): + global CACHE_DIRTY KNOWN_EXTERNAL_USERS[user_id] = time.time() - save_cache() + CACHE_DIRTY = True def is_local_room(room_id): try: @@ -153,21 +170,19 @@ def get_role(user_id): return "admin" if user_id in config.admin_users else "user" # ============================================================ -# RATE LIMIT +# RATE LIMIT (IMPROVED) # ============================================================ -def is_rate_limited(domain): +def is_rate_limited(domain, sender): + key = f"{domain}:{sender}" now = time.time() - RATE_LIMIT[domain] = [ - t for t in RATE_LIMIT[domain] - if now - t < 60 - ] + RATE_LIMIT[key] = [t for t in RATE_LIMIT[key] if now - t < 60] - if len(RATE_LIMIT[domain]) >= config.rate_limit_per_minute: + if len(RATE_LIMIT[key]) >= config.rate_limit_per_minute: return True - RATE_LIMIT[domain].append(now) + RATE_LIMIT[key].append(now) return False # ============================================================ @@ -290,7 +305,15 @@ def is_likely_dm_event(event): @app.route("/healthz") def health(): - return {"status": "ok"} + return { + "status": "ok", + "known_users": len(KNOWN_EXTERNAL_USERS), + "metrics": dict(METRICS) + } + +@app.route("/metrics") +def metrics(): + return dict(METRICS) @app.route('/_matrix/client/v3/createRoom', methods=['POST']) def create_room(): @@ -309,6 +332,7 @@ def create_room(): ) if not allowed: + METRICS["create_room_blocked"] += 1 log_event( "create_room_blocked", actor=user_id, @@ -319,6 +343,7 @@ def create_room(): return Response(json.dumps({"errcode": "M_FORBIDDEN"}), status=403) if is_dm: + METRICS["create_room_allowed"] += 1 log_event( "create_room_allowed", actor=user_id, @@ -346,7 +371,7 @@ def invite(room_id, event_id): domain = extract_domain(sender) - if is_rate_limited(domain): + if is_rate_limited(domain, sender): return Response(status=429) if domain in config.domain_whitelist: @@ -365,6 +390,7 @@ def invite(room_id, event_id): if is_user_in_local_rooms(sender): remember_user(sender) else: + METRICS["invite_blocked"] += 1 log_event( "invite_blocked", actor=sender, @@ -374,6 +400,7 @@ def invite(room_id, event_id): return Response(status=403) remember_user(sender) + METRICS["invite_allowed"] += 1 return forward_request( "PUT", @@ -421,5 +448,6 @@ if __name__ == '__main__': seed_known_users() threading.Thread(target=periodic_seed, daemon=True).start() + threading.Thread(target=periodic_cache_save, daemon=True).start() app.run(host='0.0.0.0', port=5000)