mirror of
https://github.com/ChrispyBacon-dev/DockFlare.git
synced 2026-04-28 03:39:32 +00:00
hotfixes - details in changelog.md
This commit is contained in:
parent
98eb827107
commit
c3337890e9
25 changed files with 226 additions and 66 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
## [v3.0.0] - 2025-09-25
|
||||
|
||||
### Fixed (Hotfixes)
|
||||
|
||||
#### **Agent Container Label Processing**
|
||||
- **Fixed access policy parsing** for Agent containers - containers with `dockflare.access.policy=authenticate` now display correct policy instead of "None (Public)"
|
||||
- **Added support for all documented labels** in Agent processing including `dockflare.no_tls_verify`, `dockflare.originsrvname`, `dockflare.httpHostHeader`
|
||||
- **Implemented indexed labels support** for Agent containers (e.g., `dockflare.0.hostname`, `dockflare.1.service`) for multiple domain configurations
|
||||
- **Enhanced backwards compatibility** - Agent containers now fully support legacy `cloudflare.tunnel.*` label formats
|
||||
|
||||
#### **Migration Logic Improvements**
|
||||
- **Fixed tunnel migration analysis** - containers with legacy `cloudflare.tunnel.*` labels are now properly recognized during migration instead of showing as "orphaned"
|
||||
- **Enhanced migration service compatibility** with backwards compatible label checking across all migration functions
|
||||
- **Improved container matching logic** for more accurate auto-import and conflict detection
|
||||
|
||||
#### **Dependencies Security Update**
|
||||
- **Updated Redis client** from 4.5.1 to 4.5.5 to address security vulnerabilities (CVE-2023-28859, CVE-2023-28858)
|
||||
|
||||
---
|
||||
## [v3.0.0] - 2025-09-23
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# app/__init__.py
|
||||
#
|
||||
# dockflare/app/__init__.py
|
||||
import logging
|
||||
import queue
|
||||
import sys
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/config.py
|
||||
# dockflare/app/config.py
|
||||
import os
|
||||
import logging
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/access_manager.py
|
||||
# dockflare/app/core/access_manager.py
|
||||
import logging
|
||||
import json
|
||||
import hashlib
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""Encrypted persistence for agent API keys."""
|
||||
|
||||
#
|
||||
# dockflare/app/core/agent_key_store.py
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -1,9 +1,20 @@
|
|||
# DockFlare: Automates Cloudflare Tunnel ingress from Docker labels.
|
||||
# Copyright (C) 2025 ChrispyBacon-Dev
|
||||
# Copyright (C) 2025 ChrispyBacon-Dev <https://github.com/ChrispyBacon-dev/DockFlare>
|
||||
#
|
||||
# Licensed under the GPL v3 or later.
|
||||
"""Utility helpers for creating and restoring DockFlare backup archives."""
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# dockflare/app/core/backup_manager.py
|
||||
import datetime as _dt
|
||||
import hashlib
|
||||
import io
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/cache.py
|
||||
|
||||
# dockflare/app/core/cache.py
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/cloudflare_api.py
|
||||
# dockflare/app/core/cloudflare_api.py
|
||||
import logging
|
||||
import requests
|
||||
import json
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/docker_handler.py
|
||||
# dockflare/app/core/docker_handler.py
|
||||
import logging
|
||||
import time
|
||||
import requests
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#
|
||||
# dockflare/app/core/migration_service.py
|
||||
import logging
|
||||
from typing import Dict, List, Any, Optional, Tuple
|
||||
from app.core.cloudflare_api import get_tunnel_configuration, parse_tunnel_rules_for_migration
|
||||
|
|
@ -55,8 +56,11 @@ class TunnelMigrationService:
|
|||
container_lookup = {}
|
||||
for container in agent_containers:
|
||||
labels = container.get("labels", {})
|
||||
if labels.get("dockflare.enable") == "true":
|
||||
hostname = labels.get("dockflare.hostname")
|
||||
|
||||
if (labels.get("dockflare.enable") == "true" or
|
||||
labels.get("cloudflare.tunnel.enable") == "true"):
|
||||
|
||||
hostname = labels.get("dockflare.hostname") or labels.get("cloudflare.tunnel.hostname")
|
||||
if hostname:
|
||||
container_lookup[hostname] = container
|
||||
|
||||
|
|
@ -92,7 +96,8 @@ class TunnelMigrationService:
|
|||
new_containers = []
|
||||
for container in agent_containers:
|
||||
labels = container.get("labels", {})
|
||||
hostname = labels.get("dockflare.hostname")
|
||||
|
||||
hostname = labels.get("dockflare.hostname") or labels.get("cloudflare.tunnel.hostname")
|
||||
if hostname and hostname not in [r["hostname"] for r in tunnel_rules]:
|
||||
new_containers.append(container)
|
||||
|
||||
|
|
@ -111,15 +116,7 @@ class TunnelMigrationService:
|
|||
|
||||
@staticmethod
|
||||
def execute_auto_import(migration_analysis: Dict[str, Any]) -> Tuple[int, List[str]]:
|
||||
"""
|
||||
Execute the auto-import phase of migration.
|
||||
|
||||
Args:
|
||||
migration_analysis: Results from analyze_tunnel_for_migration
|
||||
|
||||
Returns:
|
||||
Tuple of (imported_count, error_messages)
|
||||
"""
|
||||
auto_import_rules = migration_analysis.get("auto_import", [])
|
||||
if not auto_import_rules:
|
||||
return 0, []
|
||||
|
|
@ -153,20 +150,18 @@ class TunnelMigrationService:
|
|||
|
||||
@staticmethod
|
||||
def _extract_service_from_container(container: Dict) -> Optional[str]:
|
||||
"""Extract the service URL from a container's DockFlare labels."""
|
||||
"""Extract the service URL from a container's DockFlare labels (supports legacy labels)."""
|
||||
labels = container.get("labels", {})
|
||||
|
||||
# Check for explicit service label
|
||||
service = labels.get("dockflare.service")
|
||||
|
||||
service = labels.get("dockflare.service") or labels.get("cloudflare.tunnel.service")
|
||||
if service:
|
||||
return service
|
||||
|
||||
# Try to construct from individual components
|
||||
port = labels.get("dockflare.port")
|
||||
port = labels.get("dockflare.port") or labels.get("cloudflare.tunnel.port")
|
||||
if not port:
|
||||
return None
|
||||
|
||||
protocol = labels.get("dockflare.protocol", "http")
|
||||
protocol = labels.get("dockflare.protocol") or labels.get("cloudflare.tunnel.protocol") or "http"
|
||||
container_name = container.get("name", "").lstrip("/")
|
||||
|
||||
if container_name:
|
||||
|
|
@ -199,17 +194,7 @@ class TunnelMigrationService:
|
|||
|
||||
@staticmethod
|
||||
def trigger_migration_analysis(agent_id: str, tunnel_id: str, agent_containers: List[Dict]) -> Dict:
|
||||
"""
|
||||
Trigger migration analysis and auto-import for an agent.
|
||||
|
||||
Args:
|
||||
agent_id: The agent ID
|
||||
tunnel_id: The tunnel ID the agent is assigned to
|
||||
agent_containers: List of containers from agent
|
||||
|
||||
Returns:
|
||||
Migration results including what was auto-imported and any conflicts
|
||||
"""
|
||||
try:
|
||||
|
||||
analysis = TunnelMigrationService.analyze_tunnel_for_migration(tunnel_id, agent_containers)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/reconciler.py
|
||||
# dockflare/app/core/reconciler.py
|
||||
import logging
|
||||
import time
|
||||
import threading
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/state_manager.py
|
||||
# dockflare/app/core/state_manager.py
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/tunnel_manager.py
|
||||
# dockflare/app/core/tunnel_manager.py
|
||||
import copy
|
||||
import logging
|
||||
import json
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/user.py
|
||||
# dockflare/app/core/user.py
|
||||
from flask_login import UserMixin
|
||||
class User(UserMixin):
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# app/core/utils.py
|
||||
# dockflare/app/core/utils.py
|
||||
from app import config
|
||||
def get_rule_key(hostname, path):
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# app/main.py
|
||||
#
|
||||
# dockflare/app/main.py
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ from app.core.access_manager import (
|
|||
create_cloudflare_access_application,
|
||||
update_cloudflare_access_application,
|
||||
generate_access_app_config_hash,
|
||||
find_cloudflare_access_application_by_hostname
|
||||
find_cloudflare_access_application_by_hostname,
|
||||
handle_access_policy_from_labels
|
||||
)
|
||||
from app.core.reconciler import reconcile_state_threaded
|
||||
from app.core.docker_handler import is_valid_hostname, is_valid_service
|
||||
|
|
@ -158,7 +159,6 @@ def get_overview_data():
|
|||
|
||||
all_account_tunnels_list_api = get_all_account_cloudflare_tunnels()
|
||||
tunnel_names_map = {}
|
||||
# Build quick lookup for tunnel status by ID from Cloudflare API
|
||||
tunnel_status_map = {}
|
||||
try:
|
||||
for t in all_account_tunnels_list_api or []:
|
||||
|
|
@ -266,7 +266,7 @@ def get_overview_data():
|
|||
|
||||
processed["online"] = online
|
||||
processed["health"] = "connected" if online else "disconnected"
|
||||
# Enrich with tunnel status from Cloudflare if available
|
||||
|
||||
try:
|
||||
assigned_tid = processed.get("assigned_tunnel_id")
|
||||
if assigned_tid:
|
||||
|
|
@ -606,6 +606,22 @@ def process_agent_container_start(payload, agent_id):
|
|||
|
||||
default_path_label = get_label(labels, "path")
|
||||
default_originsrvname_label = get_label(labels, "originsrvname")
|
||||
default_http_host_header_label = get_label(labels, "httpHostHeader")
|
||||
|
||||
default_access_groups = get_label(labels, "access.groups")
|
||||
default_access_group = get_label(labels, "access.group") if not default_access_groups else None
|
||||
if default_access_groups:
|
||||
default_access_group = [gid.strip() for gid in default_access_groups.split(',')]
|
||||
elif default_access_group:
|
||||
default_access_group = [default_access_group.strip()]
|
||||
|
||||
default_access_policy_type_label = get_label(labels, "access.policy")
|
||||
default_access_app_name_label = get_label(labels, "access.name")
|
||||
default_access_session_duration_label = get_label(labels, "access.session_duration", "24h")
|
||||
default_access_app_launcher_visible_label = get_label(labels, "access.app_launcher_visible", "false").lower() in ["true", "1", "t", "yes"]
|
||||
default_access_allowed_idps_label_str = get_label(labels, "access.allowed_idps")
|
||||
default_access_auto_redirect_label = get_label(labels, "access.auto_redirect_to_identity", "false").lower() in ["true", "1", "t", "yes"]
|
||||
default_access_custom_rules_label_str = get_label(labels, "access.custom_rules")
|
||||
|
||||
hostname_label = get_label(labels, "hostname")
|
||||
service_label = get_label(labels, "service")
|
||||
|
|
@ -621,8 +637,75 @@ def process_agent_container_start(payload, agent_id):
|
|||
"path": default_path_label,
|
||||
"no_tls_verify": no_tls_verify_label,
|
||||
"origin_server_name": default_originsrvname_label.strip() if default_originsrvname_label else None,
|
||||
"http_host_header": default_http_host_header_label.strip() if default_http_host_header_label else None,
|
||||
"access_group": default_access_group,
|
||||
"access_policy_type": default_access_policy_type_label,
|
||||
"access_app_name": default_access_app_name_label,
|
||||
"access_session_duration": default_access_session_duration_label,
|
||||
"access_app_launcher_visible": default_access_app_launcher_visible_label,
|
||||
"access_allowed_idps_str": default_access_allowed_idps_label_str,
|
||||
"access_auto_redirect": default_access_auto_redirect_label,
|
||||
"access_custom_rules_str": default_access_custom_rules_label_str
|
||||
})
|
||||
|
||||
|
||||
index = 0
|
||||
while True:
|
||||
hostname_indexed = get_label(labels, f"{index}.hostname")
|
||||
if not hostname_indexed:
|
||||
break
|
||||
|
||||
service_indexed = get_label(labels, f"{index}.service", service_label)
|
||||
if not service_indexed:
|
||||
logging.warning(f"AGENT_PROCESS: Indexed hostname {hostname_indexed} for {container_name} missing service, skipping index {index}.")
|
||||
index += 1
|
||||
continue
|
||||
|
||||
path_indexed = get_label(labels, f"{index}.path", default_path_label)
|
||||
zone_name_indexed = get_label(labels, f"{index}.zonename", zone_name_label)
|
||||
no_tls_verify_indexed_val = get_label(labels, f"{index}.no_tls_verify", str(no_tls_verify_label).lower())
|
||||
no_tls_verify_indexed = no_tls_verify_indexed_val.lower() in ["true", "1", "t", "yes"]
|
||||
originsrvname_indexed_val = get_label(labels, f"{index}.originsrvname", default_originsrvname_label)
|
||||
http_host_header_indexed_val = get_label(labels, f"{index}.httpHostHeader", default_http_host_header_label)
|
||||
|
||||
access_groups_indexed = get_label(labels, f"{index}.access.groups")
|
||||
access_group_indexed = get_label(labels, f"{index}.access.group") if not access_groups_indexed else None
|
||||
if access_groups_indexed:
|
||||
access_group_indexed = [gid.strip() for gid in access_groups_indexed.split(',')]
|
||||
elif access_group_indexed:
|
||||
access_group_indexed = [access_group_indexed.strip()]
|
||||
else:
|
||||
access_group_indexed = default_access_group
|
||||
|
||||
access_policy_type_indexed = get_label(labels, f"{index}.access.policy", default_access_policy_type_label)
|
||||
access_app_name_indexed = get_label(labels, f"{index}.access.name", default_access_app_name_label)
|
||||
access_session_duration_indexed = get_label(labels, f"{index}.access.session_duration", default_access_session_duration_label)
|
||||
acc_launcher_val_idx = get_label(labels, f"{index}.access.app_launcher_visible", str(default_access_app_launcher_visible_label).lower())
|
||||
access_app_launcher_visible_indexed = acc_launcher_val_idx.lower() in ["true", "1", "t", "yes"]
|
||||
access_allowed_idps_indexed_str = get_label(labels, f"{index}.access.allowed_idps", default_access_allowed_idps_label_str)
|
||||
acc_redirect_val_idx = get_label(labels, f"{index}.access.auto_redirect_to_identity", str(default_access_auto_redirect_label).lower())
|
||||
access_auto_redirect_indexed = acc_redirect_val_idx.lower() in ["true", "1", "t", "yes"]
|
||||
access_custom_rules_indexed_str = get_label(labels, f"{index}.access.custom_rules", default_access_custom_rules_label_str)
|
||||
|
||||
if is_valid_hostname(hostname_indexed) and is_valid_service(service_indexed):
|
||||
hostnames_to_process.append({
|
||||
"hostname": hostname_indexed,
|
||||
"service": service_indexed,
|
||||
"zone_name": zone_name_indexed,
|
||||
"path": path_indexed,
|
||||
"no_tls_verify": no_tls_verify_indexed,
|
||||
"origin_server_name": originsrvname_indexed_val.strip() if originsrvname_indexed_val else None,
|
||||
"http_host_header": http_host_header_indexed_val.strip() if http_host_header_indexed_val else None,
|
||||
"access_group": access_group_indexed,
|
||||
"access_policy_type": access_policy_type_indexed,
|
||||
"access_app_name": access_app_name_indexed,
|
||||
"access_session_duration": access_session_duration_indexed,
|
||||
"access_app_launcher_visible": access_app_launcher_visible_indexed,
|
||||
"access_allowed_idps_str": access_allowed_idps_indexed_str,
|
||||
"access_auto_redirect": access_auto_redirect_indexed,
|
||||
"access_custom_rules_str": access_custom_rules_indexed_str
|
||||
})
|
||||
index += 1
|
||||
|
||||
if not hostnames_to_process:
|
||||
logging.warning(f"AGENT_PROCESS: No valid hostname configs for {container_name} ({container_id[:12]}).")
|
||||
return
|
||||
|
|
@ -689,6 +772,10 @@ def process_agent_container_start(payload, agent_id):
|
|||
if existing_rule.get("origin_server_name") != origin_server_name_from_item:
|
||||
existing_rule["origin_server_name"] = origin_server_name_from_item
|
||||
rule_data_changed = True
|
||||
http_host_header_from_item = config_item.get("http_host_header")
|
||||
if existing_rule.get("http_host_header") != http_host_header_from_item:
|
||||
existing_rule["http_host_header"] = http_host_header_from_item
|
||||
rule_data_changed = True
|
||||
if existing_rule.get("tunnel_name") != assigned_tunnel_name:
|
||||
existing_rule["tunnel_name"] = assigned_tunnel_name
|
||||
rule_data_changed = True
|
||||
|
|
@ -724,12 +811,14 @@ def process_agent_container_start(payload, agent_id):
|
|||
"zone_name": zone_name_from_item,
|
||||
"no_tls_verify": no_tls_verify_from_item,
|
||||
"origin_server_name": origin_server_name_from_item,
|
||||
"http_host_header": config_item.get("http_host_header"),
|
||||
"access_app_id": None,
|
||||
"access_policy_type": None,
|
||||
"access_app_config_hash": None,
|
||||
"access_policy_ui_override": False,
|
||||
"rule_ui_override": False,
|
||||
"source": "agent",
|
||||
"access_group_id": None,
|
||||
"agent_id": agent_id,
|
||||
"tunnel_name": assigned_tunnel_name,
|
||||
"tunnel_id": assigned_tunnel_id
|
||||
|
|
@ -737,6 +826,13 @@ def process_agent_container_start(payload, agent_id):
|
|||
state_changed_locally = True
|
||||
needs_tunnel_config_update = True
|
||||
|
||||
if existing_rule:
|
||||
if existing_rule.get("access_policy_ui_override", False):
|
||||
logging.info(f"AGENT_PROCESS: Access policy for {rule_key} is UI-managed. Skipping.")
|
||||
else:
|
||||
if handle_access_policy_from_labels(config_item, existing_rule, save_state):
|
||||
state_changed_locally = True
|
||||
|
||||
if state_changed_locally:
|
||||
save_state()
|
||||
publish_state_event('snapshot_refresh')
|
||||
|
|
@ -747,8 +843,7 @@ def process_agent_container_start(payload, agent_id):
|
|||
agent_record = get_agent(agent_id)
|
||||
if agent_record and agent_record.get("assigned_tunnel_id"):
|
||||
agent_tunnel_id = agent_record.get("assigned_tunnel_id")
|
||||
|
||||
# Create DNS records
|
||||
|
||||
for config_item in hostnames_to_process:
|
||||
hostname = config_item["hostname"]
|
||||
zone_name_dns_item = config_item.get("zone_name")
|
||||
|
|
@ -931,8 +1026,7 @@ def agents_register():
|
|||
display_name = data.get('display_name') or data.get('hostname') or f"agent-{agent_id[:8]}"
|
||||
version = data.get('version')
|
||||
now = datetime.utcnow().replace(tzinfo=timezone.utc).isoformat()
|
||||
|
||||
# --- Start: DockFlare Agent State Preservation Fix ---
|
||||
|
||||
existing_agent_id = provided_agent_id or agent_id
|
||||
existing_agent = get_agent(existing_agent_id)
|
||||
|
||||
|
|
@ -1011,7 +1105,6 @@ def agents_post_events(agent_id):
|
|||
"""
|
||||
Agents POST events (container start/stop, tunnel status).
|
||||
Auth via API key.
|
||||
For now we store the last_event in agent record for inspection. Later we'll process to create rules.
|
||||
"""
|
||||
logging.info(f"AGENTS_EVENTS: Received request for agent {agent_id}")
|
||||
token, owner = _authenticate_agent_request()
|
||||
|
|
@ -1051,8 +1144,7 @@ def agents_post_events(agent_id):
|
|||
logging.info(f"AGENTS_EVENTS: Processing status_report from agent {agent_id}")
|
||||
|
||||
containers = payload.get("containers") or (payload.get("container", {}) or {}).get("containers") or []
|
||||
|
||||
# Store container data in agent record for migration analysis
|
||||
|
||||
try:
|
||||
update_agent(agent_id, {"last_containers": containers})
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,21 @@
|
|||
# DockFlare: Automates Cloudflare Tunnel ingress from Docker labels.
|
||||
# Copyright (C) 2025 ChrispyBacon-Dev <https://github.com/ChrispyBacon-dev/DockFlare>
|
||||
#
|
||||
# This program is free software: you can redistribute and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# dockflare/app/web/utils.py
|
||||
import logging
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, abort
|
||||
from flask_login import login_user, logout_user, current_user
|
||||
from flask_wtf import FlaskForm
|
||||
|
|
|
|||
|
|
@ -1,7 +1,20 @@
|
|||
# DockFlare: Automates Cloudflare Tunnel ingress from Docker labels.
|
||||
# Copyright (C) 2025 ChrispyBacon-Dev
|
||||
# Copyright (C) 2025 ChrispyBacon-Dev <https://github.com/ChrispyBacon-dev/DockFlare>
|
||||
#
|
||||
# Licensed under the GPL v3 or later.
|
||||
# This program is free software: you can redistribute and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# dockflare/app/core/migration_service.py
|
||||
"""Helpers for reading and applying encrypted DockFlare configuration."""
|
||||
|
||||
import json
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# app/web/forms.py
|
||||
#
|
||||
# dockflare/app/web/forms.py
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import BooleanField, PasswordField, SubmitField, StringField, IntegerField
|
||||
from wtforms.validators import DataRequired, EqualTo, Length, Optional
|
||||
|
|
|
|||
|
|
@ -1,3 +1,20 @@
|
|||
# DockFlare: Automates Cloudflare Tunnel ingress from Docker labels.
|
||||
# Copyright (C) 2025 ChrispyBacon-Dev <https://github.com/ChrispyBacon-dev/DockFlare>
|
||||
#
|
||||
# This program is free software: you can redistribute and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# dockflare/app/web/help_routes.py
|
||||
import os
|
||||
import re
|
||||
import markdown
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# app/web/routes.py
|
||||
#
|
||||
# dockflare/app/web/routes.py
|
||||
import copy
|
||||
import logging
|
||||
import time
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# app/web/setup_routes.py
|
||||
#
|
||||
# dockflare/app/web/setup_routes.py
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@
|
|||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# /app/web/utils.py
|
||||
#
|
||||
# dockflare/app/web/utils.py
|
||||
from urllib.parse import urlparse, urljoin
|
||||
from flask import request
|
||||
from app import config
|
||||
|
|
|
|||
|
|
@ -61,5 +61,5 @@ pymdown-extensions==10.16.1
|
|||
Pygments==2.19.2
|
||||
|
||||
# Redis Caching
|
||||
redis==4.5.1
|
||||
redis==4.5.5
|
||||
flask-caching==2.1.0
|
||||
Loading…
Add table
Add a link
Reference in a new issue