|
@@ -891,6 +891,7 @@ def api_cluster_adblock():
|
|
|
subprocess.run(["systemctl", "restart", config.BIND_SERVICE])
|
|
subprocess.run(["systemctl", "restart", config.BIND_SERVICE])
|
|
|
return jsonify({"status": "success", "message": "RPZ synced on slave."})
|
|
return jsonify({"status": "success", "message": "RPZ synced on slave."})
|
|
|
|
|
|
|
|
|
|
+# --- ΠΡΟΣΘΗΚΗ SLAVE ZONE ΜΕ ΣΩΣΤΑ IPs ---
|
|
|
@app.route('/api/v1/cluster/zone', methods=['POST'])
|
|
@app.route('/api/v1/cluster/zone', methods=['POST'])
|
|
|
@api_required
|
|
@api_required
|
|
|
def api_add_slave():
|
|
def api_add_slave():
|
|
@@ -900,17 +901,12 @@ def api_add_slave():
|
|
|
|
|
|
|
|
domain = re.sub(r'[^a-zA-Z0-9\.\-\/]', '', data['domain']).replace('../', '')
|
|
domain = re.sub(r'[^a-zA-Z0-9\.\-\/]', '', data['domain']).replace('../', '')
|
|
|
|
|
|
|
|
- # --- Η ΝΕΑ ΛΟΓΙΚΗ ---
|
|
|
|
|
- # 1. Αν ο Master μας έστειλε λίστα, τη χρησιμοποιούμε αυτούσια
|
|
|
|
|
if data.get('masters'):
|
|
if data.get('masters'):
|
|
|
masters_list = data['masters']
|
|
masters_list = data['masters']
|
|
|
else:
|
|
else:
|
|
|
- # 2. Fallback στην IP αυτού που μας κάλεσε (αν δεν στάλθηκαν IPs)
|
|
|
|
|
master_ip = request.headers.get('X-Forwarded-For', request.remote_addr).split(',')[0].strip()
|
|
master_ip = request.headers.get('X-Forwarded-For', request.remote_addr).split(',')[0].strip()
|
|
|
masters_list = [master_ip]
|
|
masters_list = [master_ip]
|
|
|
|
|
|
|
|
- # Μετατροπή της λίστας σε string για το BIND config (π.χ. "1.1.1.1; 2.2.2.2;")
|
|
|
|
|
- #
|
|
|
|
|
masters_string = "".join([f" {ip};" for ip in masters_list])
|
|
masters_string = "".join([f" {ip};" for ip in masters_list])
|
|
|
|
|
|
|
|
conf = fm.read_file(config.LOCAL_CONF)
|
|
conf = fm.read_file(config.LOCAL_CONF)
|
|
@@ -923,8 +919,7 @@ def api_add_slave():
|
|
|
fm.write_file(config.LOCAL_CONF, conf)
|
|
fm.write_file(config.LOCAL_CONF, conf)
|
|
|
subprocess.run(["rndc", "reconfig"])
|
|
subprocess.run(["rndc", "reconfig"])
|
|
|
fm.log_audit("API_SYNC", domain, f"Auto-created Slave (Masters: {masters_string})")
|
|
fm.log_audit("API_SYNC", domain, f"Auto-created Slave (Masters: {masters_string})")
|
|
|
-
|
|
|
|
|
- return jsonify({"status": "success", "message": f"Slave zone {domain} configured with masters: {masters_string}"})
|
|
|
|
|
|
|
+ return jsonify({"status": "success", "message": f"Zone synced."})
|
|
|
|
|
|
|
|
@app.route('/api/v1/cluster/zone/<domain>', methods=['DELETE'])
|
|
@app.route('/api/v1/cluster/zone/<domain>', methods=['DELETE'])
|
|
|
@api_required
|
|
@api_required
|
|
@@ -1004,33 +999,24 @@ def api_cluster_service():
|
|
|
@app.route('/cluster/node_service', methods=['POST'])
|
|
@app.route('/cluster/node_service', methods=['POST'])
|
|
|
@login_required
|
|
@login_required
|
|
|
def cluster_node_service():
|
|
def cluster_node_service():
|
|
|
- # Fix: Απαραίτητο import για να μην σκάει η urllib αν πάρει Error από τον Slave
|
|
|
|
|
import urllib.request, urllib.error
|
|
import urllib.request, urllib.error
|
|
|
-
|
|
|
|
|
try:
|
|
try:
|
|
|
data = request.get_json(force=True, silent=True) or {}
|
|
data = request.get_json(force=True, silent=True) or {}
|
|
|
- node_name = data.get('node_name')
|
|
|
|
|
- action = data.get('action')
|
|
|
|
|
-
|
|
|
|
|
- node = next((n for n in fm.load_panel_settings().get('cluster_nodes', []) if n['name'] == node_name), None)
|
|
|
|
|
- if not node:
|
|
|
|
|
- return jsonify({"status": "error", "message": "Node not found."})
|
|
|
|
|
|
|
+ node = next((n for n in fm.load_panel_settings().get('cluster_nodes', []) if n['name'] == data.get('node_name')), None)
|
|
|
|
|
+ if not node: return jsonify({"status": "error", "message": "Node not found."})
|
|
|
|
|
|
|
|
url = f"{node['url'].rstrip('/')}/api/v1/cluster/service"
|
|
url = f"{node['url'].rstrip('/')}/api/v1/cluster/service"
|
|
|
- payload = {"action": action}
|
|
|
|
|
|
|
+ req = urllib.request.Request(url, data=json.dumps({"action": data.get('action')}).encode('utf-8'), headers={"X-API-Key": node['api_key'], "Content-Type": "application/json"}, method='POST')
|
|
|
|
|
|
|
|
proxy_handler = urllib.request.ProxyHandler({})
|
|
proxy_handler = urllib.request.ProxyHandler({})
|
|
|
https_handler = urllib.request.HTTPSHandler(context=config.ssl_ctx)
|
|
https_handler = urllib.request.HTTPSHandler(context=config.ssl_ctx)
|
|
|
opener = urllib.request.build_opener(proxy_handler, https_handler)
|
|
opener = urllib.request.build_opener(proxy_handler, https_handler)
|
|
|
|
|
|
|
|
- req = urllib.request.Request(url, data=json.dumps(payload).encode('utf-8'), headers={"X-API-Key": node['api_key'], "Content-Type": "application/json"}, method='POST')
|
|
|
|
|
-
|
|
|
|
|
- # Fix: Timeout στα 15 δευτερόλεπτα! Το BIND μπορεί να κάνει ώρα να κάνει restart
|
|
|
|
|
with opener.open(req, timeout=15) as response:
|
|
with opener.open(req, timeout=15) as response:
|
|
|
return jsonify(json.loads(response.read().decode('utf-8')))
|
|
return jsonify(json.loads(response.read().decode('utf-8')))
|
|
|
|
|
|
|
|
except urllib.error.HTTPError as e:
|
|
except urllib.error.HTTPError as e:
|
|
|
- return jsonify({"status": "error", "message": f"Slave returned HTTP Error {e.code}. Check API keys."})
|
|
|
|
|
|
|
+ return jsonify({"status": "error", "message": f"Slave HTTP Error {e.code}."})
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
return jsonify({"status": "error", "message": f"Connection failed: {str(e)}"})
|
|
return jsonify({"status": "error", "message": f"Connection failed: {str(e)}"})
|
|
|
|
|
|