api update
This commit is contained in:
77
api.py
77
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()
|
||||
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,43 +184,12 @@ 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()
|
||||
except Exception as pattern_error:
|
||||
print(f"[REDIS] Pattern '{pattern}' failed: {pattern_error}", flush=True)
|
||||
continue
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
@@ -215,18 +200,10 @@ def cache_status():
|
||||
'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)
|
||||
'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
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'enabled': True,
|
||||
'error': str(e)
|
||||
}), 500
|
||||
|
||||
@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'
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -94,6 +94,217 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cache Status - Redis-->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header api-header-get" onclick="toggleEndpoint('endpoint-cache-redis-status')">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="badge bg-info me-2">GET</span>
|
||||
<code class="api-path">/api/cache/redis/status</code>
|
||||
<span class="ms-3 text-muted">Redis L1 cache</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body collapse" id="endpoint-cache-redis-status">
|
||||
<h6 class="fw-bold">Description</h6>
|
||||
<p>Redis (L1: configs/networks)</p>
|
||||
|
||||
<h6 class="fw-bold mt-3">Response Schema</h6>
|
||||
<pre><code>{
|
||||
"success": <span class="text-success">true</span>,
|
||||
"enabled": <span class="text-success">true</span>,
|
||||
"health": {
|
||||
"connected": <span class="text-success">true</span>,
|
||||
"memory_peak_mb": <span class="text-info">474.25</span>,
|
||||
"memory_used_mb": <span class="text-info">241.38</span>,
|
||||
"status": <span class="text-success">"healthy"</span>
|
||||
},
|
||||
"stats": {
|
||||
"country_keys": <span class="text-info">119</span>,
|
||||
"config_keys": <span class="text-info">0</span>,
|
||||
"total_keys": <span class="text-info">119</span>,
|
||||
"total_size_mb": <span class="text-info">240.4</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<h6 class="fw-bold mt-3">Fields</h6>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>enabled</code></td>
|
||||
<td>boolean</td>
|
||||
<td>Redis configured (REDISENABLED=false)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>health.connected</code></td>
|
||||
<td>boolean</td>
|
||||
<td>TCP connection OK</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>health.memory_peak_mb</code></td>
|
||||
<td>float</td>
|
||||
<td>Redis peak memory usage</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>stats.country_keys</code></td>
|
||||
<td>integer</td>
|
||||
<td><code>geobancountry*</code> keys (119 networks cached)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>stats.config_keys</code></td>
|
||||
<td>integer</td>
|
||||
<td><code>geoipconfig*</code> + <code>geobanconfig*</code> (0 configs)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h6 class="fw-bold mt-3">Cache Flow</h6>
|
||||
<div class="alert alert-info">
|
||||
<strong>L1 Redis:</strong> configs/networks (<1s) →
|
||||
<strong>L2 SQLite:</strong> fallback networks →
|
||||
<strong>L3 MaxMind:</strong> live scan
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mt-3">Error Handling</h6>
|
||||
<div class="alert alert-warning">
|
||||
Redis offline? → Returns partial stats + <code>"error": "Connection refused"</code><br>
|
||||
Logs: <code>ConnectionError: Error 111 connecting to localhost:6379</code>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mt-3">Try it out</h6>
|
||||
<button class="btn btn-sm btn-primary" onclick="tryEndpoint('cache/redis/status')">
|
||||
<i class="fas fa-play me-1"></i>Execute
|
||||
</button>
|
||||
|
||||
<div id="response-cache-redis-status" class="mt-3" style="display:none;">
|
||||
<h6 class="fw-bold">Response</h6>
|
||||
<pre><code id="response-cache-redis-status-body"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SQLite Status (L2 Cache) -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header api-header-get" onclick="toggleEndpoint('endpoint-cache-sqlite-status')">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="badge bg-info me-2">GET</span>
|
||||
<code class="api-path">/api/cache/sqlite/status</code>
|
||||
<span class="ms-3 text-muted">SQLite L2 cache</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body collapse" id="endpoint-cache-sqlite-status">
|
||||
<h6 class="fw-bold">Description</h6>
|
||||
<p>SQLite L2 cache (`networks_cache.db`): country networks from MaxMind scans.</p>
|
||||
|
||||
<h6 class="fw-bold mt-3">Response Schema</h6>
|
||||
<pre><code>{
|
||||
"success": <span class="text-success">true</span>,
|
||||
"exists": <span class="text-success">true</span>,
|
||||
"file_size_mb": <span class="text-info">1439.54</span>,
|
||||
"total_countries": <span class="text-info">123</span>,
|
||||
"total_networks": <span class="text-info">13728494</span>,
|
||||
"top_countries": [
|
||||
{"code": <span class="text-warning">"US"</span>, "networks": <span class="text-info">5801506</span>}
|
||||
]
|
||||
}</code></pre>
|
||||
|
||||
<h6 class="fw-bold mt-3">Fields</h6>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Type</th><th>Description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td><code>exists</code></td><td>boolean</td><td>DB file exists</td></tr>
|
||||
<tr><td><code>file_size_mb</code></td><td>float</td><td>DB size on disk</td></tr>
|
||||
<tr><td><code>total_countries</code></td><td>integer</td><td>Countries with cached networks</td></tr>
|
||||
<tr><td><code>top_countries</code></td><td>array</td><td>Top 5 by network count</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h6 class="fw-bold mt-3">Try it out</h6>
|
||||
<button class="btn btn-sm btn-primary" onclick="tryEndpoint('cache/sqlite/status')">
|
||||
<i class="fas fa-play me-1"></i>Execute
|
||||
</button>
|
||||
|
||||
<!-- ID: response-cache-sqlite-status -->
|
||||
<div id="response-cache-sqlite-status" class="mt-3" style="display:none;">
|
||||
<h6 class="fw-bold">Response</h6>
|
||||
<pre><code id="response-cache-sqlite-status-body"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cache Invalidate -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header api-header-post" onclick="toggleEndpoint('endpoint-cache-invalidate')">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="badge bg-success me-2">POST</span>
|
||||
<code class="api-path">/api/cache/invalidate/<country_code></code>
|
||||
<span class="ms-3 text-muted">Invalidate country cache</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body collapse" id="endpoint-cache-invalidate">
|
||||
<h6 class="fw-bold">Description</h6>
|
||||
<p>Clears Redis L1 cache for specific country: networks + all configs containing it. Forces fresh SQLite/MaxMind scan next time.</p>
|
||||
|
||||
<h6 class="fw-bold mt-3">Path Parameter</h6>
|
||||
<table class="table table-sm">
|
||||
<thead><tr><th>Name</th><th>Type</th><th>Description</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><code>country_code</code></td><td>string</td><td>ISO 3166-1 alpha-2 (e.g. <code>CN</code>, <code>RU</code>)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h6 class="fw-bold mt-3">Response Schema</h6>
|
||||
<pre><code>{
|
||||
"success": <span class="text-success">true</span>,
|
||||
"deleted": <span class="text-info">5</span>,
|
||||
"country": <span class="text-warning">"CN"</span>,
|
||||
"details": {
|
||||
"country_cache": <span class="text-info">1</span>,
|
||||
"config_caches": <span class="text-info">4</span>
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h6 class="fw-bold mt-3">What it deletes</h6>
|
||||
<ul>
|
||||
<li><code>geoban:country:CN</code> - country networks</li>
|
||||
<li><code>geoban:config:*[CN]</code> - configs with CN</li>
|
||||
</ul>
|
||||
|
||||
<h6 class="fw-bold mt-3">Try it out</h6>
|
||||
<p>Enter country code (e.g. <code>CN</code>):
|
||||
<input type="text" id="invalidateCountry" class="form-control form-control-sm d-inline w-auto" maxlength="2" placeholder="CN">
|
||||
<button class="btn btn-sm btn-warning ms-2" onclick="tryInvalidateCountry()">
|
||||
<i class="fas fa-trash me-1"></i>Invalidate
|
||||
</button>
|
||||
</p>
|
||||
|
||||
<div id="response-cache-invalidate" class="mt-3" style="display:none;">
|
||||
<h6 class="fw-bold">Response</h6>
|
||||
<pre><code id="response-cache-invalidate-body"></code></pre>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mt-3">cURL Example</h6>
|
||||
<pre><code>curl -X POST <span id="curlUrl-invalidate"></span>/api/cache/invalidate/CN</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Endpoint 3: Update Database -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header api-header-post" onclick="toggleEndpoint('endpoint3')">
|
||||
|
||||
Reference in New Issue
Block a user