117 lines
3.6 KiB
Python
117 lines
3.6 KiB
Python
from flask import Flask, render_template, request, jsonify, abort
|
|
import requests
|
|
import threading
|
|
import secrets
|
|
import os
|
|
import hashlib
|
|
import time
|
|
from functools import wraps
|
|
|
|
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 = {}
|
|
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('/')
|
|
def index():
|
|
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)
|
|
|
|
@app.route('/api/update', methods=['POST'])
|
|
@require_api_key
|
|
def api_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"]
|
|
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:
|
|
deleted_servers.remove(server_name)
|
|
|
|
return jsonify({"message": "Updated successfully"}), 200
|
|
|
|
# Legacy endpoint for backward compatibility
|
|
@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
|
|
server_name = data.get("server")
|
|
if not server_name or server_name not in proxy_data:
|
|
return jsonify({"error": "Server not found"}), 400
|
|
|
|
deleted_servers.add(server_name)
|
|
return jsonify({"message": f"Server {server_name} deleted"}), 200
|
|
|
|
# Legacy endpoint for backward compatibility
|
|
@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"""
|
|
try:
|
|
response = requests.get(f"https://{domain}", timeout=3)
|
|
return jsonify({"status": response.status_code})
|
|
except requests.exceptions.RequestException:
|
|
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__':
|
|
app.run(host='0.0.0.0', port=5000, ssl_context='adhoc') # Using adhoc SSL for development
|