diff --git a/api.py b/api.py index 0c3528e..34fd922 100644 --- a/api.py +++ b/api.py @@ -139,7 +139,7 @@ def get_countries(): 'countries': config.COMMON_COUNTRIES }) -@api_blueprint.route('/api/cache/status', methods=['GET']) +@api_blueprint.route('/api/cache/redis/status', methods=['GET']) def cache_status(): if not redis_cache: return jsonify({ @@ -148,19 +148,35 @@ def cache_status(): 'message': 'Redis cache is not enabled' }) + health = None try: health = redis_cache.health_check() - - country_keys_count = 0 - config_keys_count = 0 - total_size_bytes = 0 - + except Exception as health_error: + print(f"[REDIS] Health check failed: {health_error}", flush=True) + health = {'connected': False, 'status': 'disconnected', 'error': str(health_error)} + + country_keys_count = 0 + config_keys_count = 0 + total_size_bytes = 0 + + patterns = [ + ("geoban:country:*", "country"), + ("geoip:config:*", "config"), + ("geoban:config:*", "config") + ] + + for pattern, key_type in patterns: try: - pattern_country = "geoban:country:*" cursor = 0 while True: - cursor, keys = redis_cache.redis_client.scan(cursor, match=pattern_country, count=1000) - country_keys_count += len(keys) + cursor, keys = redis_cache.redis_client.scan(cursor, match=pattern, count=1000) + + key_count = len(keys) + if key_type == "country": + country_keys_count += key_count + else: + config_keys_count += key_count + for key in keys: try: size = redis_cache.redis_client.memory_usage(key) @@ -168,65 +184,26 @@ def cache_status(): total_size_bytes += size except: pass + if cursor == 0: break - - pattern_config = "geoip:config:*" - cursor = 0 - while True: - cursor, keys = redis_cache.redis_client.scan(cursor, match=pattern_config, count=1000) - config_keys_count += len(keys) - for key in keys: - try: - size = redis_cache.redis_client.memory_usage(key) - if size: - total_size_bytes += size - except: - pass - if cursor == 0: - break - - pattern_config_new = "geoban:config:*" - cursor = 0 - while True: - cursor, keys = redis_cache.redis_client.scan(cursor, match=pattern_config_new, count=1000) - config_keys_count += len(keys) - for key in keys: - try: - size = redis_cache.redis_client.memory_usage(key) - if size: - total_size_bytes += size - except: - pass - if cursor == 0: - break - - except Exception as e: - print(f"[REDIS] Error counting keys: {e}", flush=True) - import traceback - traceback.print_exc() - - return jsonify({ - 'success': True, - 'enabled': True, - 'health': health, - 'stats': { - 'country_keys': country_keys_count, - 'config_keys': config_keys_count, - 'total_keys': country_keys_count + config_keys_count, - 'total_size_mb': round(total_size_bytes / 1024 / 1024, 2), - 'memory_used_mb': health.get('memory_used_mb', 0), - 'total_keys_in_db': health.get('keys', 0) - } - }) - except Exception as e: - import traceback - traceback.print_exc() - return jsonify({ - 'success': False, - 'enabled': True, - 'error': str(e) - }), 500 + except Exception as pattern_error: + print(f"[REDIS] Pattern '{pattern}' failed: {pattern_error}", flush=True) + continue + + return jsonify({ + 'success': True, + 'enabled': True, + 'health': health, + 'stats': { + 'country_keys': country_keys_count, + 'config_keys': config_keys_count, + 'total_keys': country_keys_count + config_keys_count, + 'total_size_mb': round(total_size_bytes / 1024 / 1024, 2), + 'memory_used_mb': health.get('memory_used_mb', 0) if isinstance(health, dict) else 0, + 'total_keys_in_db': health.get('keys', 0) if isinstance(health, dict) else 0 + } + }) @api_blueprint.route('/api/cache/flush', methods=['POST']) def cache_flush(): @@ -700,7 +677,7 @@ def generate_config(): clear_progress() return jsonify({'success': False, 'error': str(e)}), 500 -@api_blueprint.route('/api/database/sqlite/status', methods=['GET']) +@api_blueprint.route('/api/cache/sqlite/status', methods=['GET']) def sqlite_status(): """Get SQLite cache database statistics""" db_path = config.GEOIP_DB_DIR / 'networks_cache.db' diff --git a/static/js/cache.js b/static/js/cache.js index 1602fce..7f2bf87 100644 --- a/static/js/cache.js +++ b/static/js/cache.js @@ -12,8 +12,8 @@ async function loadCacheStats() { try { const [cacheResponse, sqliteResponse] = await Promise.all([ - fetch('/api/cache/status'), - fetch('/api/database/sqlite/status') + fetch('/api/cache/redis/status'), + fetch('/api/cache/sqlite/status') ]); const cacheData = await cacheResponse.json(); diff --git a/templates/api.html b/templates/api.html index ac2b5e0..bdbc1fc 100644 --- a/templates/api.html +++ b/templates/api.html @@ -94,6 +94,217 @@ + +
+
+
+
+ GET + /api/cache/redis/status + Redis L1 cache +
+ +
+
+
+
Description
+

Redis (L1: configs/networks)

+ +
Response Schema
+
{
+  "success": true,
+  "enabled": true,
+  "health": {
+    "connected": true,
+    "memory_peak_mb": 474.25,
+    "memory_used_mb": 241.38,
+    "status": "healthy"
+  },
+  "stats": {
+    "country_keys": 119,
+    "config_keys": 0,
+    "total_keys": 119,
+    "total_size_mb": 240.4
+  }
+}
+          
+ +
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
enabledbooleanRedis configured (REDISENABLED=false)
health.connectedbooleanTCP connection OK
health.memory_peak_mbfloatRedis peak memory usage
stats.country_keysintegergeobancountry* keys (119 networks cached)
stats.config_keysintegergeoipconfig* + geobanconfig* (0 configs)
+ +
Cache Flow
+
+ L1 Redis: configs/networks (<1s) → + L2 SQLite: fallback networks → + L3 MaxMind: live scan +
+ +
Error Handling
+
+ Redis offline? → Returns partial stats + "error": "Connection refused"
+ Logs: ConnectionError: Error 111 connecting to localhost:6379 +
+ +
Try it out
+ + + +
+
+ + +
+
+
+
+ GET + /api/cache/sqlite/status + SQLite L2 cache +
+ +
+
+
+
Description
+

SQLite L2 cache (`networks_cache.db`): country networks from MaxMind scans.

+ +
Response Schema
+
{
+  "success": true,
+  "exists": true,
+  "file_size_mb": 1439.54,
+  "total_countries": 123,
+  "total_networks": 13728494,
+  "top_countries": [
+    {"code": "US", "networks": 5801506}
+  ]
+}
+ +
Fields
+ + + + + + + + + + +
NameTypeDescription
existsbooleanDB file exists
file_size_mbfloatDB size on disk
total_countriesintegerCountries with cached networks
top_countriesarrayTop 5 by network count
+ +
Try it out
+ + + + +
+
+ + +
+
+
+
+ POST + /api/cache/invalidate/<country_code> + Invalidate country cache +
+ +
+
+
+
Description
+

Clears Redis L1 cache for specific country: networks + all configs containing it. Forces fresh SQLite/MaxMind scan next time.

+ +
Path Parameter
+ + + + + +
NameTypeDescription
country_codestringISO 3166-1 alpha-2 (e.g. CN, RU)
+ +
Response Schema
+
{
+  "success": true,
+  "deleted": 5,
+  "country": "CN",
+  "details": {
+    "country_cache": 1,
+    "config_caches": 4
+  }
+}
+ +
What it deletes
+ + +
Try it out
+

Enter country code (e.g. CN): + + +

+ + + +
cURL Example
+
curl -X POST /api/cache/invalidate/CN
+
+
+