mirror of
https://github.com/ChrispyBacon-dev/DockFlare.git
synced 2026-04-28 03:39:32 +00:00
HTML optimizations, waitress performance tuning
This commit is contained in:
parent
0125e1245e
commit
04f9321ab1
8 changed files with 249 additions and 120 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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 %}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue