batman dev
This commit is contained in:
parent
a4ce8a291d
commit
de000c7ac6
10 changed files with 389 additions and 34 deletions
|
@ -7,6 +7,12 @@ RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
COPY . /app/
|
COPY . /app/
|
||||||
|
|
||||||
|
# Create a volume for persistent data
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
|
|
||||||
|
# Set environment variable for API key (should be overridden at runtime)
|
||||||
|
ENV CADDYDB_API_KEY=""
|
||||||
|
|
||||||
CMD ["python", "app.py"]
|
CMD ["python", "app.py"]
|
||||||
|
|
86
app.py
86
app.py
|
@ -1,33 +1,83 @@
|
||||||
from flask import Flask, render_template, request, jsonify
|
from flask import Flask, render_template, request, jsonify, abort
|
||||||
import requests
|
import requests
|
||||||
import threading
|
import threading
|
||||||
|
import secrets
|
||||||
|
import os
|
||||||
|
import hashlib
|
||||||
|
import time
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Generate a secure API key if not already set in environment
|
||||||
|
if not os.environ.get('CADDYDB_API_KEY'):
|
||||||
|
app.config['API_KEY'] = os.environ.get('CADDYDB_API_KEY', secrets.token_hex(32))
|
||||||
|
print(f"API Key: {app.config['API_KEY']} - Store this securely!")
|
||||||
|
else:
|
||||||
|
app.config['API_KEY'] = os.environ.get('CADDYDB_API_KEY')
|
||||||
|
|
||||||
proxy_data = {}
|
proxy_data = {}
|
||||||
deleted_servers = set()
|
deleted_servers = set()
|
||||||
|
|
||||||
|
# Authentication decorator
|
||||||
|
def require_api_key(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
api_key = request.headers.get('X-API-Key')
|
||||||
|
if not api_key or api_key != app.config['API_KEY']:
|
||||||
|
return jsonify({"error": "Unauthorized access"}), 401
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
filtered_data = {k: v for k, v in proxy_data.items() if k not in deleted_servers}
|
filtered_data = {k: v for k, v in proxy_data.items() if k not in deleted_servers}
|
||||||
return render_template('index.html', proxies=filtered_data)
|
return render_template('index.html', proxies=filtered_data)
|
||||||
|
|
||||||
@app.route('/update', methods=['POST'])
|
@app.route('/api/update', methods=['POST'])
|
||||||
def update():
|
@require_api_key
|
||||||
|
def api_update():
|
||||||
data = request.json
|
data = request.json
|
||||||
if not data or "server" not in data or "entries" not in data:
|
if not data or "server" not in data or "entries" not in data:
|
||||||
return jsonify({"error": "Invalid data"}), 400
|
return jsonify({"error": "Invalid data"}), 400
|
||||||
|
|
||||||
server_name = data["server"]
|
server_name = data["server"]
|
||||||
proxy_data[server_name] = data["entries"]
|
source_type = data.get("source_type", "caddy") # Default to caddy if not specified
|
||||||
|
|
||||||
|
proxy_data[server_name] = {
|
||||||
|
"entries": data["entries"],
|
||||||
|
"source_type": source_type,
|
||||||
|
"last_updated": time.time()
|
||||||
|
}
|
||||||
|
|
||||||
if server_name in deleted_servers:
|
if server_name in deleted_servers:
|
||||||
deleted_servers.remove(server_name)
|
deleted_servers.remove(server_name)
|
||||||
|
|
||||||
return jsonify({"message": "Updated successfully"}), 200
|
return jsonify({"message": "Updated successfully"}), 200
|
||||||
|
|
||||||
@app.route('/delete', methods=['POST'])
|
# Legacy endpoint for backward compatibility
|
||||||
def delete_entry():
|
@app.route('/update', methods=['POST'])
|
||||||
|
@require_api_key
|
||||||
|
def update():
|
||||||
|
data = request.json
|
||||||
|
if not data or "server" not in data or "entries" not in data:
|
||||||
|
return jsonify({"error": "Invalid data"}), 400
|
||||||
|
|
||||||
|
server_name = data["server"]
|
||||||
|
proxy_data[server_name] = {
|
||||||
|
"entries": data["entries"],
|
||||||
|
"source_type": "caddy",
|
||||||
|
"last_updated": time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
if server_name in deleted_servers:
|
||||||
|
deleted_servers.remove(server_name)
|
||||||
|
|
||||||
|
return jsonify({"message": "Updated successfully"}), 200
|
||||||
|
|
||||||
|
@app.route('/api/delete', methods=['POST'])
|
||||||
|
@require_api_key
|
||||||
|
def api_delete_entry():
|
||||||
data = request.json
|
data = request.json
|
||||||
server_name = data.get("server")
|
server_name = data.get("server")
|
||||||
if not server_name or server_name not in proxy_data:
|
if not server_name or server_name not in proxy_data:
|
||||||
|
@ -36,8 +86,14 @@ def delete_entry():
|
||||||
deleted_servers.add(server_name)
|
deleted_servers.add(server_name)
|
||||||
return jsonify({"message": f"Server {server_name} deleted"}), 200
|
return jsonify({"message": f"Server {server_name} deleted"}), 200
|
||||||
|
|
||||||
@app.route('/status/<domain>')
|
# Legacy endpoint for backward compatibility
|
||||||
def check_status(domain):
|
@app.route('/delete', methods=['POST'])
|
||||||
|
@require_api_key
|
||||||
|
def delete_entry():
|
||||||
|
return api_delete_entry()
|
||||||
|
|
||||||
|
@app.route('/api/status/<domain>')
|
||||||
|
def api_check_status(domain):
|
||||||
"""Check if a subdomain is reachable"""
|
"""Check if a subdomain is reachable"""
|
||||||
try:
|
try:
|
||||||
response = requests.get(f"https://{domain}", timeout=3)
|
response = requests.get(f"https://{domain}", timeout=3)
|
||||||
|
@ -45,5 +101,17 @@ def check_status(domain):
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
return jsonify({"status": "offline"})
|
return jsonify({"status": "offline"})
|
||||||
|
|
||||||
|
# Legacy endpoint for backward compatibility
|
||||||
|
@app.route('/status/<domain>')
|
||||||
|
def check_status(domain):
|
||||||
|
return api_check_status(domain)
|
||||||
|
|
||||||
|
@app.route('/api/servers', methods=['GET'])
|
||||||
|
@require_api_key
|
||||||
|
def list_servers():
|
||||||
|
"""List all registered servers"""
|
||||||
|
filtered_data = {k: v for k, v in proxy_data.items() if k not in deleted_servers}
|
||||||
|
return jsonify({"servers": list(filtered_data.keys())}), 200
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(host='0.0.0.0', port=5000)
|
app.run(host='0.0.0.0', port=5000, ssl_context='adhoc') # Using adhoc SSL for development
|
||||||
|
|
|
@ -2,4 +2,9 @@ services:
|
||||||
caddydb:
|
caddydb:
|
||||||
image: caddydb:latest
|
image: caddydb:latest
|
||||||
ports:
|
ports:
|
||||||
- 5000:5000
|
- "5000:5000"
|
||||||
|
environment:
|
||||||
|
- CADDYDB_API_KEY=${CADDYDB_API_KEY:-changeme} # Set this to a secure value
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
restart: unless-stopped
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
flask
|
flask
|
||||||
requests
|
requests
|
||||||
|
pyyaml
|
||||||
|
toml
|
||||||
|
pyopenssl
|
||||||
|
|
|
@ -5,10 +5,30 @@ command_exists() {
|
||||||
command -v "$@" >/dev/null 2>&1
|
command -v "$@" >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptPath=""
|
# Default values
|
||||||
|
scriptPath="/app/src/update.py"
|
||||||
|
apiKey="${CADDYDB_API_KEY:-}"
|
||||||
|
dashboardUrl="${CADDYDB_URL:-http://10.0.0.25:5000/api/update}"
|
||||||
|
|
||||||
|
# Check if API key is set
|
||||||
|
if [ -z "$apiKey" ]; then
|
||||||
|
echo "Warning: CADDYDB_API_KEY environment variable is not set."
|
||||||
|
echo "Please set it before running this script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if command_exists python3; then
|
if command_exists python3; then
|
||||||
echo "*/10 * * * * /usr/bin/python3 ${scriptPath}" | crontab -
|
# Create a wrapper script that includes the API key
|
||||||
|
wrapperScript="/tmp/caddydb_update_wrapper.sh"
|
||||||
|
echo "#!/bin/bash" > "$wrapperScript"
|
||||||
|
echo "export CADDYDB_API_KEY=\"$apiKey\"" >> "$wrapperScript"
|
||||||
|
echo "python3 $scriptPath --url $dashboardUrl" >> "$wrapperScript"
|
||||||
|
chmod +x "$wrapperScript"
|
||||||
|
|
||||||
|
# Add to crontab
|
||||||
|
echo "*/10 * * * * $wrapperScript" | crontab -
|
||||||
|
echo "Added cron job to update CaddyDB every 10 minutes"
|
||||||
else
|
else
|
||||||
echo "No python was found.."
|
echo "No python was found.."
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
90
src/nginx_update.py
Normal file
90
src/nginx_update.py
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import re
|
||||||
|
import socket
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
|
||||||
|
# Default configuration
|
||||||
|
NGINX_CONF_DIR = "/etc/nginx/sites-enabled"
|
||||||
|
DASHBOARD_URL = "http://10.0.0.25:5000/api/update"
|
||||||
|
SERVER_NAME = socket.gethostname()
|
||||||
|
API_KEY = os.environ.get('CADDYDB_API_KEY', '')
|
||||||
|
|
||||||
|
def parse_nginx_configs(conf_dir):
|
||||||
|
entries = {}
|
||||||
|
|
||||||
|
# Get all config files
|
||||||
|
config_files = glob.glob(f"{conf_dir}/*")
|
||||||
|
|
||||||
|
for config_file in config_files:
|
||||||
|
try:
|
||||||
|
with open(config_file, "r") as file:
|
||||||
|
content = file.read()
|
||||||
|
|
||||||
|
# Extract server_name and proxy_pass
|
||||||
|
server_blocks = re.findall(r'server\s*{[^}]*}', content, re.DOTALL)
|
||||||
|
|
||||||
|
for block in server_blocks:
|
||||||
|
server_names = re.search(r'server_name\s+([^;]+);', block)
|
||||||
|
proxy_pass = re.search(r'proxy_pass\s+([^;]+);', block)
|
||||||
|
|
||||||
|
if server_names and proxy_pass:
|
||||||
|
domains = server_names.group(1).strip().split()
|
||||||
|
target = proxy_pass.group(1).strip()
|
||||||
|
|
||||||
|
for domain in domains:
|
||||||
|
if domain != '_': # Skip default server
|
||||||
|
entries[domain] = target
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading Nginx config {config_file}: {e}")
|
||||||
|
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def send_update(url, api_key, server_name, entries):
|
||||||
|
data = {
|
||||||
|
"server": server_name,
|
||||||
|
"entries": entries,
|
||||||
|
"source_type": "nginx"
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {"X-API-Key": api_key}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print(f"Successfully updated {server_name} data")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"Error: {response.status_code} - {response.text}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending update: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Update CaddyDB with Nginx reverse proxy entries')
|
||||||
|
parser.add_argument('--url', default=DASHBOARD_URL, help='CaddyDB API URL')
|
||||||
|
parser.add_argument('--dir', default=NGINX_CONF_DIR, help='Path to Nginx config directory')
|
||||||
|
parser.add_argument('--server', default=SERVER_NAME, help='Server name')
|
||||||
|
parser.add_argument('--key', default=API_KEY, help='API key')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not args.key:
|
||||||
|
print("Error: API key is required. Set CADDYDB_API_KEY environment variable or use --key")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
entries = parse_nginx_configs(args.dir)
|
||||||
|
if not entries:
|
||||||
|
print("No entries found in Nginx configurations")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
success = send_update(args.url, args.key, args.server, entries)
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
115
src/traefik_update.py
Normal file
115
src/traefik_update.py
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import socket
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import yaml
|
||||||
|
import toml
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Default configuration
|
||||||
|
TRAEFIK_CONF_PATH = "/etc/traefik/traefik.yml"
|
||||||
|
DASHBOARD_URL = "http://10.0.0.25:5000/api/update"
|
||||||
|
SERVER_NAME = socket.gethostname()
|
||||||
|
API_KEY = os.environ.get('CADDYDB_API_KEY', '')
|
||||||
|
|
||||||
|
def parse_traefik_config(conf_path):
|
||||||
|
entries = {}
|
||||||
|
|
||||||
|
# Determine file type
|
||||||
|
if conf_path.endswith(('.yml', '.yaml')):
|
||||||
|
try:
|
||||||
|
with open(conf_path, 'r') as file:
|
||||||
|
config = yaml.safe_load(file)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading YAML config: {e}")
|
||||||
|
return entries
|
||||||
|
elif conf_path.endswith('.toml'):
|
||||||
|
try:
|
||||||
|
with open(conf_path, 'r') as file:
|
||||||
|
config = toml.load(file)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading TOML config: {e}")
|
||||||
|
return entries
|
||||||
|
elif conf_path.endswith('.json'):
|
||||||
|
try:
|
||||||
|
with open(conf_path, 'r') as file:
|
||||||
|
config = json.load(file)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading JSON config: {e}")
|
||||||
|
return entries
|
||||||
|
else:
|
||||||
|
print(f"Unsupported file format: {conf_path}")
|
||||||
|
return entries
|
||||||
|
|
||||||
|
# Try to extract router configurations
|
||||||
|
try:
|
||||||
|
# For static configuration
|
||||||
|
if 'http' in config and 'routers' in config['http']:
|
||||||
|
for router_name, router in config['http']['routers'].items():
|
||||||
|
if 'rule' in router and 'service' in router:
|
||||||
|
# Extract host from rule (assuming Host rule)
|
||||||
|
host_match = router['rule']
|
||||||
|
if 'Host(' in host_match:
|
||||||
|
domain = host_match.split('Host(')[1].split(')')[0].replace('`', '').strip()
|
||||||
|
service = router['service']
|
||||||
|
entries[domain] = f"traefik service: {service}"
|
||||||
|
|
||||||
|
# For dynamic file providers
|
||||||
|
if 'providers' in config and 'file' in config['providers']:
|
||||||
|
file_path = config['providers']['file'].get('filename')
|
||||||
|
if file_path:
|
||||||
|
# Recursively parse the dynamic config
|
||||||
|
dynamic_entries = parse_traefik_config(file_path)
|
||||||
|
entries.update(dynamic_entries)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error parsing Traefik config: {e}")
|
||||||
|
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def send_update(url, api_key, server_name, entries):
|
||||||
|
data = {
|
||||||
|
"server": server_name,
|
||||||
|
"entries": entries,
|
||||||
|
"source_type": "traefik"
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {"X-API-Key": api_key}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print(f"Successfully updated {server_name} data")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"Error: {response.status_code} - {response.text}")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending update: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Update CaddyDB with Traefik reverse proxy entries')
|
||||||
|
parser.add_argument('--url', default=DASHBOARD_URL, help='CaddyDB API URL')
|
||||||
|
parser.add_argument('--config', default=TRAEFIK_CONF_PATH, help='Path to Traefik config file')
|
||||||
|
parser.add_argument('--server', default=SERVER_NAME, help='Server name')
|
||||||
|
parser.add_argument('--key', default=API_KEY, help='API key')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not args.key:
|
||||||
|
print("Error: API key is required. Set CADDYDB_API_KEY environment variable or use --key")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
entries = parse_traefik_config(args.config)
|
||||||
|
if not entries:
|
||||||
|
print("No entries found in Traefik configuration")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
success = send_update(args.url, args.key, args.server, entries)
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -2,17 +2,21 @@
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import re
|
import re
|
||||||
# import os
|
|
||||||
import socket
|
import socket
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
|
||||||
CADDYFILE_PATH = "/opt/docker/caddy/conf/Caddyfile" # Pfad zur Caddyfile
|
# Default configuration
|
||||||
DASHBOARD_URL = "http://10.0.0.25:5000/update" # Anpassen!
|
CADDYFILE_PATH = "/opt/docker/caddy/conf/Caddyfile"
|
||||||
|
DASHBOARD_URL = "http://10.0.0.25:5000/api/update"
|
||||||
SERVER_NAME = socket.gethostname()
|
SERVER_NAME = socket.gethostname()
|
||||||
|
API_KEY = os.environ.get('CADDYDB_API_KEY', '')
|
||||||
|
|
||||||
def parse_caddyfile():
|
def parse_caddyfile(file_path):
|
||||||
entries = {}
|
entries = {}
|
||||||
try:
|
try:
|
||||||
with open(CADDYFILE_PATH, "r") as file:
|
with open(file_path, "r") as file:
|
||||||
content = file.read()
|
content = file.read()
|
||||||
|
|
||||||
pattern = re.compile(r"(?P<domains>[^\s{]+(?:,\s*[^\s{]+)*)\s*{.*?reverse_proxy\s+(?P<target>https?:\/\/[\d\.]+:\d+|[\d\.]+:\d+).*?}", re.DOTALL)
|
pattern = re.compile(r"(?P<domains>[^\s{]+(?:,\s*[^\s{]+)*)\s*{.*?reverse_proxy\s+(?P<target>https?:\/\/[\d\.]+:\d+|[\d\.]+:\d+).*?}", re.DOTALL)
|
||||||
|
@ -22,16 +26,51 @@ def parse_caddyfile():
|
||||||
for domain in domains.split(", "):
|
for domain in domains.split(", "):
|
||||||
entries[domain] = target.strip()
|
entries[domain] = target.strip()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler beim Lesen der Caddyfile: {e}")
|
print(f"Error reading Caddyfile: {e}")
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
def send_update():
|
def send_update(url, api_key, server_name, entries, source_type="caddy"):
|
||||||
data = {"server": SERVER_NAME, "entries": parse_caddyfile()}
|
data = {
|
||||||
|
"server": server_name,
|
||||||
|
"entries": entries,
|
||||||
|
"source_type": source_type
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {"X-API-Key": api_key}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.post(DASHBOARD_URL, json=data)
|
response = requests.post(url, json=data, headers=headers)
|
||||||
print(response.json())
|
if response.status_code == 200:
|
||||||
|
print(f"Successfully updated {server_name} data")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"Error: {response.status_code} - {response.text}")
|
||||||
|
return False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler beim Senden: {e}")
|
print(f"Error sending update: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Update CaddyDB with reverse proxy entries')
|
||||||
|
parser.add_argument('--url', default=DASHBOARD_URL, help='CaddyDB API URL')
|
||||||
|
parser.add_argument('--file', default=CADDYFILE_PATH, help='Path to Caddyfile')
|
||||||
|
parser.add_argument('--server', default=SERVER_NAME, help='Server name')
|
||||||
|
parser.add_argument('--key', default=API_KEY, help='API key')
|
||||||
|
parser.add_argument('--source', default="caddy", help='Source type (caddy, nginx, traefik)')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not args.key:
|
||||||
|
print("Error: API key is required. Set CADDYDB_API_KEY environment variable or use --key")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
entries = parse_caddyfile(args.file)
|
||||||
|
if not entries:
|
||||||
|
print("No entries found in Caddyfile")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
success = send_update(args.url, args.key, args.server, entries, args.source)
|
||||||
|
sys.exit(0 if success else 1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
send_update()
|
main()
|
||||||
|
|
0
templates/base.html
Normal file
0
templates/base.html
Normal file
|
@ -1,5 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
@ -26,8 +27,8 @@
|
||||||
function deleteServer(serverName) {
|
function deleteServer(serverName) {
|
||||||
fetch('/delete', {
|
fetch('/delete', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({"server": serverName})
|
body: JSON.stringify({ "server": serverName })
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
document.getElementById("server-box-" + serverName).remove();
|
document.getElementById("server-box-" + serverName).remove();
|
||||||
|
@ -35,7 +36,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("keydown", function(event) {
|
document.addEventListener("keydown", function (event) {
|
||||||
if (event.key === "/") {
|
if (event.key === "/") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
toggleSearch();
|
toggleSearch();
|
||||||
|
@ -43,29 +44,36 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-gray-900 text-gray-100">
|
<body class="bg-gray-900 text-gray-100">
|
||||||
|
|
||||||
<header class="bg-indigo-700 text-white p-6 text-center">
|
<header class="bg-indigo-700 text-white p-6 text-center">
|
||||||
<h1 class="text-3xl font-bold">Caddy Dashboard</h1>
|
<h1 class="text-3xl font-bold">Caddy Dashboard</h1>
|
||||||
<p class="text-cyan-300 text-lg">Übersicht über aller aktiven Proxy-Server</p>
|
<p class="text-cyan-300 text-lg">Übersicht über aller aktiven Proxy-Server</p>
|
||||||
<button onclick="toggleSearch()" class="bg-cyan-500 hover:bg-cyan-400 text-white text-right px-6 py-3 rounded-lg mt-4 text-lg">🔍 Suche</button>
|
<button onclick="toggleSearch()"
|
||||||
|
class="bg-cyan-500 hover:bg-cyan-400 text-white text-right px-6 py-3 rounded-lg mt-4 text-lg">🔍
|
||||||
|
Suche</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="container mx-auto p-4 md:p-6">
|
<div class="container mx-auto p-4 md:p-6">
|
||||||
<input type="text" id="search-box" class="hidden w-full p-4 mb-4 text-gray-900 text-lg rounded-md" placeholder="🔍 Suche nach Subdomains.." onkeyup="filterEntries()">
|
<input type="text" id="search-box" class="hidden w-full p-4 mb-4 text-gray-900 text-lg rounded-md"
|
||||||
|
placeholder="🔍 Suche nach Subdomains.." onkeyup="filterEntries()">
|
||||||
|
|
||||||
{% for server, entries in proxies.items() %}
|
{% for server, entries in proxies.items() %}
|
||||||
<div id="server-box-{{ server }}" class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6">
|
<div id="server-box-{{ server }}" class="bg-gray-800 p-6 rounded-lg shadow-lg mb-6">
|
||||||
<div class="flex flex-col md:flex-row justify-between items-start md:items-center">
|
<div class="flex flex-col md:flex-row justify-between items-start md:items-center">
|
||||||
<h2 class="text-2xl font-semibold text-indigo-400">{{ server }}</h2>
|
<h2 class="text-2xl font-semibold text-indigo-400">{{ server }}</h2>
|
||||||
<button onclick="deleteServer('{{ server }}')" class="mt-3 md:mt-0 bg-red-500 text-white px-6 py-2 rounded-lg text-lg">🗑️ Löschen</button>
|
<button onclick="deleteServer('{{ server }}')"
|
||||||
|
class="mt-3 md:mt-0 bg-red-500 text-white px-6 py-2 rounded-lg text-lg">🗑️ Löschen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mt-4">
|
||||||
{% for domain, target in entries.items() %}
|
{% for domain, target in entries.items() %}
|
||||||
<div class="domain-card bg-gray-700 p-4 rounded-lg shadow-md flex flex-col space-y-2" data-domain="{{ domain }}">
|
<div class="domain-card bg-gray-700 p-4 rounded-lg shadow-md flex flex-col space-y-2"
|
||||||
|
data-domain="{{ domain }}">
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<a href="https://{{ domain }}" target="_blank" class="text-indigo-400 hover:text-indigo-300 text-lg break-words">{{ domain }}</a>
|
<a href="https://{{ domain }}" target="_blank"
|
||||||
|
class="text-indigo-400 hover:text-indigo-300 text-lg break-words">{{ domain }}</a>
|
||||||
<!-- <button onclick="checkStatus('{{ domain }}', this)" class="bg-cyan-500 text-white px-4 py-2 rounded-lg text-sm">🔄 Prüfen</button> -->
|
<!-- <button onclick="checkStatus('{{ domain }}', this)" class="bg-cyan-500 text-white px-4 py-2 rounded-lg text-sm">🔄 Prüfen</button> -->
|
||||||
</div>
|
</div>
|
||||||
<p class="text-gray-300 break-words text-lg">{{ target }}</p>
|
<p class="text-gray-300 break-words text-lg">{{ target }}</p>
|
||||||
|
@ -77,4 +85,5 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue