HTML optimizations, waitress performance tuning

This commit is contained in:
ChrispyBacon-dev 2025-10-06 16:48:45 +02:00
parent 0125e1245e
commit 04f9321ab1
8 changed files with 249 additions and 120 deletions

View file

@ -100,6 +100,8 @@ docker network create cloudflare-net
services:
docker-socket-proxy:
image: tecnativa/docker-socket-proxy:v0.4.1
logging:
driver: "none" # Minimize the logs, remove for verbose
container_name: docker-socket-proxy
restart: unless-stopped
environment:
@ -138,6 +140,7 @@ docker network create cloudflare-net
- REDIS_URL=redis://redis:6379/0
- REDIS_DB_INDEX=0 # Optional: specify Redis database index (0-15) for isolation from other containers
- DOCKER_HOST=tcp://docker-socket-proxy:2375
- LOG_LEVEL=ERROR
depends_on:
docker-socket-proxy:
condition: service_started

View file

@ -19,6 +19,20 @@
import os
import logging
def _get_int_env(name, default, minimum=None):
raw_value = os.getenv(name)
if raw_value is None:
return default
try:
parsed = int(raw_value)
if minimum is not None and parsed < minimum:
logging.warning(f"Environment variable {name} must be >= {minimum}. Using default {default}.")
return default
return parsed
except ValueError:
logging.warning(f"Environment variable {name} must be an integer. Using default {default}.")
return default
# --- DockFlare Version ---
APP_VERSION = "v3.0.3"
# --- web: https://dockflare.app ---
@ -79,6 +93,14 @@ RECONCILIATION_BATCH_SIZE = int(os.getenv('RECONCILIATION_BATCH_SIZE', 5))
ACCOUNT_EMAIL_CACHE_TTL = 3600
SCAN_ALL_NETWORKS = os.getenv('SCAN_ALL_NETWORKS', 'false').lower() in ['true', '1', 't', 'yes']
# Waitress / web server tuning
WAITRESS_HOST = os.getenv('WAITRESS_HOST', '0.0.0.0')
WAITRESS_PORT = _get_int_env('WAITRESS_PORT', 5000, minimum=1)
WAITRESS_THREADS = _get_int_env('WAITRESS_THREADS', 128, minimum=1)
WAITRESS_CONNECTION_LIMIT = _get_int_env('WAITRESS_CONNECTION_LIMIT', 256, minimum=1)
WAITRESS_BACKLOG = _get_int_env('WAITRESS_BACKLOG', 2048, minimum=1)
WAITRESS_CHANNEL_TIMEOUT = _get_int_env('WAITRESS_CHANNEL_TIMEOUT', 360, minimum=1)
# If set, enables the Prometheus metrics endpoint on the specified port.
# The IP is hardcoded to 0.0.0.0 to be accessible within Docker networks.
CLOUDFLARED_METRICS_PORT = os.getenv('CLOUDFLARED_METRICS_PORT')
@ -112,7 +134,7 @@ AGENT_ENROLLMENT_REQUIRED = os.getenv('AGENT_ENROLLMENT_REQUIRED', 'true').lower
AGENT_KEY_STORAGE_PATH = os.getenv('AGENT_KEY_STORAGE_PATH', None)
# Polling interval (seconds) recommended for agents to poll the Master for commands
AGENT_COMMAND_POLL_INTERVAL = int(os.getenv('AGENT_COMMAND_POLL_INTERVAL', 5))
AGENT_COMMAND_POLL_INTERVAL = int(os.getenv('AGENT_COMMAND_POLL_INTERVAL', 10))
REDIS_DB_INDEX = int(os.getenv('REDIS_DB_INDEX', 0))

View file

@ -375,15 +375,29 @@ def main_application_entrypoint():
flask_server_thread = None
try:
from waitress import serve
waitress_kwargs = {
'host': config.WAITRESS_HOST,
'port': config.WAITRESS_PORT,
'threads': config.WAITRESS_THREADS,
'expose_tracebacks': False
}
if config.WAITRESS_CONNECTION_LIMIT:
waitress_kwargs['connection_limit'] = config.WAITRESS_CONNECTION_LIMIT
if config.WAITRESS_BACKLOG:
waitress_kwargs['backlog'] = config.WAITRESS_BACKLOG
if config.WAITRESS_CHANNEL_TIMEOUT:
waitress_kwargs['channel_timeout'] = config.WAITRESS_CHANNEL_TIMEOUT
flask_server_thread = threading.Thread(
target=serve,
args=(app,),
kwargs={'host': '0.0.0.0', 'port': 5000, 'threads': 10, 'expose_tracebacks': False},
kwargs=waitress_kwargs,
daemon=True,
name="FlaskWaitressServer"
)
flask_server_thread.start()
logging.info("Flask server started using waitress on 0.0.0.0:5000.")
logging.info(f"Flask server started using waitress on {config.WAITRESS_HOST}:{config.WAITRESS_PORT} with {config.WAITRESS_THREADS} threads.")
while not stop_event.is_set():
if flask_server_thread and not flask_server_thread.is_alive():
@ -412,7 +426,7 @@ def main_application_entrypoint():
except ImportError:
logging.warning("Waitress not found. Using Flask development server (NOT FOR PRODUCTION).")
app.run(host='0.0.0.0', port=5000, threaded=True, debug=False)
app.run(host=config.WAITRESS_HOST, port=config.WAITRESS_PORT, threaded=True, debug=False)
except KeyboardInterrupt:
logging.info("KeyboardInterrupt received. Shutting down...")
except Exception as server_startup_err:

File diff suppressed because one or more lines are too long

View file

@ -1527,14 +1527,21 @@ function renderIdPTable(idps) {
};
let tableHTML = `
<table class="table table-zebra table-sm w-full">
<table class="table table-zebra table-sm policy-table w-full">
<colgroup>
<col class="col-primary">
<col class="col-secondary">
<col class="col-tertiary">
<col class="col-status">
<col class="col-actions">
</colgroup>
<thead>
<tr>
<th class="p-3">Type</th>
<th class="p-3">Name</th>
<th class="p-3">Provider Type</th>
<th class="p-3">Provider</th>
<th class="p-3">Cloudflare ID</th>
<th class="p-3">Connector</th>
<th class="p-3">Status</th>
<th class="p-3">Actions</th>
<th class="p-3 text-right">Actions</th>
</tr>
</thead>
<tbody>`;
@ -1548,11 +1555,18 @@ function renderIdPTable(idps) {
tableHTML += `
<tr>
<td class="p-3">${icon}</td>
<td class="p-3 font-medium">${idpData.name}</td>
<td class="p-3">
<div class="flex items-center gap-3">
<span class="inline-flex items-center justify-center">${icon}</span>
<div class="font-medium">${idpData.name}</div>
</div>
</td>
<td class="p-3 text-xs opacity-70">
${idpData.cloudflare_id ? `<span class="tooltip" data-tip="${idpData.cloudflare_id}"><code>${idpData.cloudflare_id.slice(0, 8)}...</code></span>` : '-'}
</td>
<td class="p-3 text-sm opacity-80">${idpData.type}</td>
<td class="p-3">${statusBadge}</td>
<td class="p-3">
<td class="p-3 text-right">
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-ghost btn-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">

View file

@ -42,7 +42,14 @@
{% if access_groups and access_groups.items() %}
<div class="overflow-x-auto table-container">
<table class="table table-zebra w-full">
<table class="table table-zebra policy-table w-full">
<colgroup>
<col class="col-primary">
<col class="col-secondary">
<col class="col-tertiary">
<col class="col-status">
<col class="col-actions">
</colgroup>
<thead>
<tr>
<th class="px-4 py-3">Display Name</th>
@ -59,11 +66,11 @@
<div class="font-medium flex items-center gap-2">
{{ details.display_name }}
{% if details.external_policy %}
<span class="badge badge-sm bg-purple-500/20 text-purple-300 border-purple-500/30">External</span>
<span class="badge badge-sm badge-secondary">External</span>
{% elif details.system_policy %}
<span class="badge badge-sm bg-orange-500/20 text-orange-300 border-orange-500/30">System</span>
<span class="badge badge-sm badge-warning">System</span>
{% else %}
<span class="badge badge-sm bg-blue-500/20 text-blue-300 border-blue-500/30">DockFlare</span>
<span class="badge badge-sm badge-info">DockFlare</span>
{% endif %}
</div>
{% if group_id in group_usage %}
@ -71,14 +78,14 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-3 h-3">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" />
</svg>
In use by {{ group_usage[group_id]|length }}
In use by {{ group_usage[group_id]|length }} service{{ '' if group_usage[group_id]|length == 1 else 's' }}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-3 h-3 chevron-icon">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</button>
{% endif %}
</td>
<td class="px-4 py-3 align-top"><code class="badge badge-ghost">{{ group_id }}</code></td>
<td class="px-4 py-3 align-top"><code class="badge badge-sm badge-outline">{{ group_id }}</code></td>
<td class="px-4 py-3 text-xs opacity-80 align-top">
{% if details.policies %}
{{ details.policies | length }} rule(s) defined
@ -103,16 +110,17 @@
</li>
{% if details.cloudflare_policy_id and ACCOUNT_ID_FOR_DISPLAY and ACCOUNT_ID_FOR_DISPLAY != "Not Configured" %}
<li>
<a href="https://one.dash.cloudflare.com/{{ ACCOUNT_ID_FOR_DISPLAY }}/access/policies/{{ details.cloudflare_policy_id }}/edit" target="_blank" rel="noopener noreferrer">
<a class="flex items-center gap-2" href="https://one.dash.cloudflare.com/{{ ACCOUNT_ID_FOR_DISPLAY }}/access/policies/{{ details.cloudflare_policy_id }}/edit" target="_blank" rel="noopener noreferrer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" /></svg>
View in Cloudflare
</a>
</li>
{% endif %}
<div class="divider my-1"></div>
<li {{ 'class="disabled"' if group_id in used_group_ids or details.get('system_policy') or not details.get('deletable', True) else '' }}>
<a class="text-error delete-access-group-btn" data-group-id="{{ group_id }}" data-group-name="{{ details.display_name }}" data-external="{{ 'true' if details.get('external_policy') else 'false' }}" {{ 'title="Cannot delete: system policy"' if details.get('system_policy') or not details.get('deletable', True) else ('title="Cannot delete: group is in use"' if group_id in used_group_ids else '') }}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /></svg>
{% set is_disabled = details.get('system_policy') or not details.get('deletable', True) or group_id in used_group_ids %}
<li class="{{ 'disabled' if is_disabled else '' }}">
<a class="text-error delete-access-group-btn {{ 'pointer-events-none cursor-not-allowed select-none' if is_disabled else '' }}" data-group-id="{{ group_id }}" data-group-name="{{ details.display_name }}" data-external="{{ 'true' if details.get('external_policy') else 'false' }}" data-disabled="{{ 'true' if is_disabled else 'false' }}" {{ 'title="Cannot delete: system policy"' if details.get('system_policy') or not details.get('deletable', True) else ('title="Cannot delete: group is in use"' if group_id in used_group_ids else '') }}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /></svg>
{% if details.get('system_policy') or not details.get('deletable', True) %}
<span class="opacity-50">Delete (system policy)</span>
{% elif group_id in used_group_ids %}
@ -125,7 +133,7 @@
{% if group_id in used_group_ids %}
<div class="divider my-1"></div>
<li class="menu-title">
<span class="text-xs opacity-70">Used by {{ group_usage[group_id]|length }} application(s)</span>
<span class="text-xs opacity-70">Used by {{ group_usage[group_id]|length }} service{{ '' if group_usage[group_id]|length == 1 else 's' }}</span>
</li>
{% endif %}
</ul>
@ -137,7 +145,7 @@
<td colspan="5" class="p-0">
<div class="bg-base-200/50 p-4 border-l-4 border-info">
<div class="text-sm font-semibold mb-2 opacity-70">
Used by these applications:
Used by these services:
</div>
<ul class="list-disc list-inside space-y-1 text-sm opacity-80">
{% for hostname in group_usage[group_id] %}
@ -197,7 +205,7 @@
<!-- 3. Zone Default Policies Section -->
<section class="card bg-base-100 shadow-xl mb-8 sm:mb-12 transition-all duration-300 hover:shadow-2xl">
<div class="card-body">
<div class="card-body overflow-visible">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center border-b border-base-300 pb-4 mb-4">
<div>
<h2 class="card-title text-2xl sm:text-3xl">
@ -347,6 +355,35 @@
.usage-details-row td {
transition: all 0.3s ease-in-out;
}
.policy-table {
table-layout: fixed;
}
.policy-table col.col-primary {
width: 28%;
}
.policy-table col.col-secondary {
width: 22%;
}
.policy-table col.col-tertiary {
width: 22%;
}
.policy-table col.col-status {
width: 14%;
}
.policy-table col.col-actions {
width: 14%;
}
.policy-table th,
.policy-table td {
vertical-align: top;
}
</style>
<script>
@ -677,7 +714,8 @@
e.preventDefault();
e.stopPropagation();
if (btn.closest('li')?.classList.contains('disabled')) {
const isDisabled = btn.dataset.disabled === 'true' || btn.closest('li')?.classList.contains('disabled');
if (isDisabled) {
return;
}
@ -781,8 +819,17 @@
return;
}
let html = '<div class="overflow-x-auto"><table class="table table-zebra w-full"><thead><tr>';
let html = '<div class="overflow-x-auto table-container"><table class="table table-zebra policy-table w-full">';
html += '<colgroup>';
html += '<col class="col-primary">';
html += '<col class="col-secondary">';
html += '<col class="col-tertiary">';
html += '<col class="col-status">';
html += '<col class="col-actions">';
html += '</colgroup>';
html += '<thead><tr>';
html += '<th class="px-4 py-3">Zone Name</th>';
html += '<th class="px-4 py-3">Zone ID</th>';
html += '<th class="px-4 py-3">Wildcard Hostname</th>';
html += '<th class="px-4 py-3">Status</th>';
html += '<th class="px-4 py-3 text-right">Actions</th>';
@ -791,6 +838,11 @@
zonePolicies.forEach(zone => {
html += '<tr>';
html += `<td class="px-4 py-3 font-medium">${zone.zone_name}</td>`;
if (zone.zone_id) {
html += `<td class="px-4 py-3 text-xs opacity-70"><span class=\"tooltip\" data-tip=\"${zone.zone_id}\"><code>${zone.zone_id.slice(0, 8)}...</code></span></td>`;
} else {
html += '<td class="px-4 py-3 text-xs opacity-70">-</td>';
}
html += `<td class="px-4 py-3"><code class="text-sm">*.${zone.zone_name}</code></td>`;
html += '<td class="px-4 py-3">';
@ -809,15 +861,41 @@
html += '</td><td class="px-4 py-3 text-right">';
if (!zone.has_default_policy) {
html += `<button class="btn btn-sm btn-primary create-zone-policy-btn" data-zone-name="${zone.zone_name}" data-zone-id="${zone.zone_id}">`;
html += '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2">';
html += '<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />';
html += '</svg>Create Policy</button>';
html += `<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-ghost btn-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />
</svg>
</label>
<ul tabindex="0" class="dropdown-content menu menu-compact bg-base-100 shadow-lg rounded-box w-60 mt-2">
<li>
<a class="create-zone-policy-btn flex items-center gap-2" data-zone-name="${zone.zone_name}" data-zone-id="${zone.zone_id}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
Create Policy
</a>
</li>
</ul>
</div>`;
} else {
html += `<a href="https://one.dash.cloudflare.com/{{ ACCOUNT_ID_FOR_DISPLAY }}/access/apps" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-ghost">`;
html += '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2">';
html += '<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />';
html += '</svg>View in CF</a>';
html += `<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-ghost btn-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />
</svg>
</label>
<ul tabindex="0" class="dropdown-content menu menu-compact bg-base-100 shadow-lg rounded-box w-60 mt-2">
<li>
<a href="https://one.dash.cloudflare.com/{{ ACCOUNT_ID_FOR_DISPLAY }}/access/apps" target="_blank" rel="noopener noreferrer" class="flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
</svg>
View in Cloudflare
</a>
</li>
</ul>
</div>`;
}
html += '</td></tr>';
@ -873,4 +951,4 @@
</script>
{% include 'modals/_idp_modal.html' %}
{% endblock %}
{% endblock %}

View file

@ -205,60 +205,72 @@ def get_overview_data():
logging.debug(f"Could not build zone lookup map: {zone_list_error}")
with state_lock:
state_changed_during_serialization = False
for hostname_key, rule_value in managed_rules.items():
serialized_rule = serialize_rule(rule_value)
tunnel_id_value = serialized_rule.get("tunnel_id")
if not tunnel_id_value and rule_value.get("source") == "manual":
fallback_tunnel_id = get_effective_tunnel_id()
if fallback_tunnel_id:
tunnel_id_value = fallback_tunnel_id
serialized_rule["tunnel_id"] = fallback_tunnel_id
rule_value["tunnel_id"] = fallback_tunnel_id
state_changed_during_serialization = True
if tunnel_id_value and (not serialized_rule.get("tunnel_name") or serialized_rule.get("tunnel_name") in (None, "", "N/A")):
tunnel_name_lookup = tunnel_names_map.get(tunnel_id_value)
if tunnel_name_lookup:
serialized_rule["tunnel_name"] = tunnel_name_lookup
elif tunnel_id_value == (tunnel_state.get("id") if not config.USE_EXTERNAL_CLOUDFLARED else config.EXTERNAL_TUNNEL_ID):
fallback_name = tunnel_state.get("name")
if fallback_name:
serialized_rule["tunnel_name"] = fallback_name
if serialized_rule.get("tunnel_name") and rule_value.get("tunnel_name") != serialized_rule.get("tunnel_name"):
rule_value["tunnel_name"] = serialized_rule["tunnel_name"]
state_changed_during_serialization = True
zone_id_value = serialized_rule.get("zone_id")
if zone_id_value and not serialized_rule.get("zone_name"):
zone_name_lookup = zone_lookup_map.get(zone_id_value)
if zone_name_lookup:
serialized_rule["zone_name"] = zone_name_lookup
if rule_value.get("zone_name") != zone_name_lookup:
rule_value["zone_name"] = zone_name_lookup
state_changed_during_serialization = True
rules_for_api[hostname_key] = serialized_rule
rules_snapshot = {hostname_key: rule_value.copy() for hostname_key, rule_value in managed_rules.items()}
api_tunnel_state = tunnel_state.copy()
api_agent_state = cloudflared_agent_state.copy()
initialization_status_api = {
"complete": api_tunnel_state.get("id") is not None or config.EXTERNAL_TUNNEL_ID,
"in_progress": not (api_tunnel_state.get("id") or config.EXTERNAL_TUNNEL_ID) and \
api_tunnel_state.get("status_message", "").lower().startswith("init")
}
initialization_status_api = {
"complete": api_tunnel_state.get("id") is not None or config.EXTERNAL_TUNNEL_ID,
"in_progress": not (api_tunnel_state.get("id") or config.EXTERNAL_TUNNEL_ID) and \
api_tunnel_state.get("status_message", "").lower().startswith("init")
}
cf_zone_id = current_app.config.get('CF_ZONE_ID')
if cf_zone_id and docker_client:
zone_details = get_zone_details_by_id(cf_zone_id)
if zone_details and zone_details.get("name"):
relevant_zone_name_for_tld_policy_api = zone_details.get("name")
if relevant_zone_name_for_tld_policy_api:
tld_policy_exists_val_api = check_for_tld_access_policy(relevant_zone_name_for_tld_policy_api)
if not tld_policy_exists_val_api:
account_email_for_tld_api = get_cloudflare_account_email()
if state_changed_during_serialization:
state_updates = {}
effective_tunnel_id_cache = None
for hostname_key, rule_snapshot in rules_snapshot.items():
serialized_rule = serialize_rule(rule_snapshot)
tunnel_id_value = serialized_rule.get("tunnel_id")
if not tunnel_id_value and rule_snapshot.get("source") == "manual":
if effective_tunnel_id_cache is None:
effective_tunnel_id_cache = get_effective_tunnel_id()
if effective_tunnel_id_cache:
tunnel_id_value = effective_tunnel_id_cache
serialized_rule["tunnel_id"] = effective_tunnel_id_cache
state_updates.setdefault(hostname_key, {})["tunnel_id"] = effective_tunnel_id_cache
if tunnel_id_value:
tunnel_name_value = serialized_rule.get("tunnel_name")
if not tunnel_name_value or tunnel_name_value in ("", "N/A"):
tunnel_name_lookup = tunnel_names_map.get(tunnel_id_value)
if not tunnel_name_lookup:
master_tunnel_id = api_tunnel_state.get("id") if not config.USE_EXTERNAL_CLOUDFLARED else config.EXTERNAL_TUNNEL_ID
if tunnel_id_value == master_tunnel_id:
tunnel_name_lookup = api_tunnel_state.get("name")
if tunnel_name_lookup:
serialized_rule["tunnel_name"] = tunnel_name_lookup
state_updates.setdefault(hostname_key, {})["tunnel_name"] = tunnel_name_lookup
zone_id_value = serialized_rule.get("zone_id")
if zone_id_value and not serialized_rule.get("zone_name"):
zone_name_lookup = zone_lookup_map.get(zone_id_value)
if zone_name_lookup:
serialized_rule["zone_name"] = zone_name_lookup
state_updates.setdefault(hostname_key, {})["zone_name"] = zone_name_lookup
rules_for_api[hostname_key] = serialized_rule
if state_updates:
state_changed = False
with state_lock:
for hostname_key, updates in state_updates.items():
rule_ref = managed_rules.get(hostname_key)
if not rule_ref:
continue
for field, value in updates.items():
if rule_ref.get(field) != value:
rule_ref[field] = value
state_changed = True
if state_changed:
save_state()
cf_zone_id = current_app.config.get('CF_ZONE_ID')
if cf_zone_id and docker_client:
zone_details = get_zone_details_by_id(cf_zone_id)
if zone_details and zone_details.get("name"):
relevant_zone_name_for_tld_policy_api = zone_details.get("name")
if relevant_zone_name_for_tld_policy_api:
tld_policy_exists_val_api = check_for_tld_access_policy(relevant_zone_name_for_tld_policy_api)
if not tld_policy_exists_val_api:
account_email_for_tld_api = get_cloudflare_account_email()
agents_list_api = list_agents()
agent_keys_list_api = list_agent_keys()
@ -292,20 +304,6 @@ def get_overview_data():
assigned_tid = processed.get("assigned_tunnel_id")
if assigned_tid:
ts = tunnel_status_map.get(assigned_tid)
if not ts:
try:
account_id = current_app.config.get('CF_ACCOUNT_ID')
if account_id:
detail = cf_api_request("GET", f"/accounts/{account_id}/cfd_tunnel/{assigned_tid}")
if detail and detail.get("result"):
res = detail["result"]
ts = {
"status": res.get("status") or "unknown",
"name": res.get("name")
}
tunnel_status_map[assigned_tid] = ts
except Exception as _fetch_e:
logging.debug(f"Could not fetch tunnel detail for {assigned_tid}: {_fetch_e}")
if ts:
existing_ts = processed.get("tunnel_status") or {}
if existing_ts.get("version") and "version" not in ts:

View file

@ -282,36 +282,36 @@ def status_page():
template_agents = {}
with state_lock:
for hostname, rule in managed_rules.items():
rule_copy = copy.deepcopy(rule)
if rule_copy.get("delete_at") and isinstance(rule_copy["delete_at"], datetime):
rule_copy["delete_at"] = rule_copy["delete_at"].replace(tzinfo=timezone.utc) if rule_copy["delete_at"].tzinfo is None else rule_copy["delete_at"].astimezone(timezone.utc)
rules_for_template[hostname] = rule_copy
rules_snapshot = {hostname: copy.deepcopy(rule) for hostname, rule in managed_rules.items()}
template_tunnel_state = tunnel_state.copy()
template_agent_state = cloudflared_agent_state.copy()
template_access_groups = copy.deepcopy(access_groups)
from app.core.state_manager import list_agents
template_agents = list_agents()
initialization_status = {
"complete": template_tunnel_state.get("id") is not None or config.EXTERNAL_TUNNEL_ID,
"in_progress": not (template_tunnel_state.get("id") or config.EXTERNAL_TUNNEL_ID) and \
template_tunnel_state.get("status_message", "").lower().startswith("init")
}
for hostname, rule_copy in rules_snapshot.items():
if rule_copy.get("delete_at") and isinstance(rule_copy["delete_at"], datetime):
rule_copy["delete_at"] = rule_copy["delete_at"].replace(tzinfo=timezone.utc) if rule_copy["delete_at"].tzinfo is None else rule_copy["delete_at"].astimezone(timezone.utc)
rules_for_template[hostname] = rule_copy
cf_zone_id = current_app.config.get('CF_ZONE_ID')
if cf_zone_id and docker_client:
initialization_status = {
"complete": template_tunnel_state.get("id") is not None or config.EXTERNAL_TUNNEL_ID,
"in_progress": not (template_tunnel_state.get("id") or config.EXTERNAL_TUNNEL_ID) and \
template_tunnel_state.get("status_message", "").lower().startswith("init")
}
zone_details = get_zone_details_by_id(cf_zone_id)
if zone_details and zone_details.get("name"):
relevant_zone_name_for_tld_policy_val = zone_details.get("name")
cf_zone_id = current_app.config.get('CF_ZONE_ID')
if cf_zone_id and docker_client:
zone_details = get_zone_details_by_id(cf_zone_id)
if zone_details and zone_details.get("name"):
relevant_zone_name_for_tld_policy_val = zone_details.get("name")
if relevant_zone_name_for_tld_policy_val:
tld_policy_exists_val = check_for_tld_access_policy(relevant_zone_name_for_tld_policy_val)
if not tld_policy_exists_val:
account_email_for_tld_val = get_cloudflare_account_email()
else:
logging.info("Relevant zone name for TLD policy check (from CF_ZONE_ID) could not be determined.")
if relevant_zone_name_for_tld_policy_val:
tld_policy_exists_val = check_for_tld_access_policy(relevant_zone_name_for_tld_policy_val)
if not tld_policy_exists_val:
account_email_for_tld_val = get_cloudflare_account_email()
else:
logging.info("Relevant zone name for TLD policy check (from CF_ZONE_ID) could not be determined.")
display_token_val = get_display_token_ui(template_tunnel_state.get("token"))
cf_account_id = current_app.config.get('CF_ACCOUNT_ID')