2025-12-30 11:30:55 -03:00
|
|
|
import glob
|
2026-01-08 13:43:51 -03:00
|
|
|
import json
|
2025-12-30 11:30:55 -03:00
|
|
|
import os
|
|
|
|
|
|
2026-01-08 11:37:51 -03:00
|
|
|
from django.conf import settings
|
2025-12-31 19:05:37 -03:00
|
|
|
from django.http import JsonResponse, FileResponse
|
2025-12-30 11:30:55 -03:00
|
|
|
from django.utils import timezone
|
2026-01-08 13:43:51 -03:00
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
2025-12-30 11:30:55 -03:00
|
|
|
|
|
|
|
|
from .models import ClusterSettings, Worker, WorkerStatus
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_ip_address(request):
|
|
|
|
|
ip_address = request.META.get('HTTP_X_FORWARDED_FOR')
|
|
|
|
|
if ip_address:
|
|
|
|
|
ip_address = ip_address.split(',')[0]
|
|
|
|
|
else:
|
|
|
|
|
ip_address = request.META.get('REMOTE_ADDR')
|
|
|
|
|
return ip_address
|
|
|
|
|
|
|
|
|
|
|
2026-01-08 11:37:51 -03:00
|
|
|
def get_cluster_settings():
|
|
|
|
|
cluster_settings, created = ClusterSettings.objects.get_or_create(name='cluster_settings')
|
|
|
|
|
return {
|
|
|
|
|
'enabled': cluster_settings.enabled,
|
|
|
|
|
'primary_enable_wireguard': cluster_settings.primary_enable_wireguard,
|
|
|
|
|
'stats_sync_interval': settings.WIREGUARD_STATUS_CACHE_REFRESH_INTERVAL,
|
|
|
|
|
'cluster_mode': cluster_settings.cluster_mode,
|
|
|
|
|
'restart_mode': cluster_settings.restart_mode,
|
|
|
|
|
'config_version': cluster_settings.config_version,
|
|
|
|
|
'dns_version': cluster_settings.dns_version,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-12-30 11:30:55 -03:00
|
|
|
def get_worker(request):
|
|
|
|
|
min_worker_version = 1
|
|
|
|
|
success = True
|
|
|
|
|
ip_address = get_ip_address(request)
|
|
|
|
|
token = request.GET.get('token', '')
|
|
|
|
|
try:
|
|
|
|
|
worker = Worker.objects.get(token=token)
|
|
|
|
|
except:
|
|
|
|
|
return None, False
|
|
|
|
|
|
|
|
|
|
worker_status, created = WorkerStatus.objects.get_or_create(worker=worker)
|
|
|
|
|
try:
|
|
|
|
|
worker_config_version = int(request.GET.get('worker_config_version'))
|
|
|
|
|
worker_version = int(request.GET.get('worker_version'))
|
2025-12-31 19:05:37 -03:00
|
|
|
worker_dns_version = int(request.GET.get('worker_dns_version'))
|
2025-12-30 11:30:55 -03:00
|
|
|
except:
|
|
|
|
|
worker.error_status = 'missing_version'
|
|
|
|
|
worker.save()
|
|
|
|
|
return worker, False
|
|
|
|
|
|
|
|
|
|
if worker.error_status == 'missing_version':
|
|
|
|
|
worker.error_status = ''
|
|
|
|
|
worker.save()
|
|
|
|
|
|
|
|
|
|
if worker_version < min_worker_version:
|
|
|
|
|
worker.error_status = 'update_required'
|
|
|
|
|
worker.save()
|
|
|
|
|
return worker, False
|
|
|
|
|
if worker.error_status == 'update_required':
|
|
|
|
|
worker.error_status = ''
|
|
|
|
|
worker.save()
|
|
|
|
|
|
|
|
|
|
if worker_status.config_version != worker_config_version:
|
|
|
|
|
worker_status.config_version = worker_config_version
|
|
|
|
|
if worker_status.worker_version != worker_version:
|
|
|
|
|
worker_status.worker_version = worker_version
|
2025-12-31 18:34:02 -03:00
|
|
|
if worker_status.dns_version != worker_dns_version:
|
|
|
|
|
worker_status.dns_version = worker_dns_version
|
2025-12-30 11:30:55 -03:00
|
|
|
worker_status.last_seen = timezone.now()
|
|
|
|
|
worker_status.save()
|
|
|
|
|
|
|
|
|
|
if not worker.ip_address:
|
|
|
|
|
worker.ip_address = ip_address
|
|
|
|
|
worker.save()
|
|
|
|
|
|
|
|
|
|
if worker.ip_lock:
|
|
|
|
|
if worker.ip_address == ip_address:
|
|
|
|
|
if worker.error_status == 'ip_lock':
|
|
|
|
|
worker.error_status = ''
|
|
|
|
|
worker.save()
|
|
|
|
|
else:
|
|
|
|
|
worker.error_status = 'ip_lock'
|
|
|
|
|
worker.save()
|
|
|
|
|
success = False
|
|
|
|
|
else:
|
|
|
|
|
if worker.ip_address != ip_address:
|
|
|
|
|
worker.ip_address = ip_address
|
|
|
|
|
worker.save()
|
|
|
|
|
|
|
|
|
|
if worker.enabled:
|
|
|
|
|
if worker.error_status == 'worker_disabled':
|
|
|
|
|
worker.error_status = ''
|
|
|
|
|
worker.save()
|
|
|
|
|
else:
|
|
|
|
|
worker.error_status = 'worker_disabled'
|
|
|
|
|
worker.save()
|
|
|
|
|
success = False
|
|
|
|
|
|
|
|
|
|
cluster_settings, created = ClusterSettings.objects.get_or_create(name='cluster_settings')
|
|
|
|
|
if cluster_settings.enabled:
|
|
|
|
|
if worker.error_status == 'cluster_disabled':
|
|
|
|
|
worker.error_status = ''
|
|
|
|
|
worker.save()
|
|
|
|
|
else:
|
|
|
|
|
worker.error_status = 'cluster_disabled'
|
|
|
|
|
worker.save()
|
|
|
|
|
success = False
|
|
|
|
|
|
|
|
|
|
return worker, success
|
|
|
|
|
|
|
|
|
|
|
2025-12-31 19:05:37 -03:00
|
|
|
def api_get_worker_dnsmasq_config(request):
|
|
|
|
|
dnsmasq_file = "/etc/dnsmasq/dnsmasq_config.tar.gz"
|
|
|
|
|
worker, success = get_worker(request)
|
|
|
|
|
if worker:
|
|
|
|
|
if worker.error_status or not success:
|
|
|
|
|
data = {'status': 'error', 'message': worker.error_status}
|
|
|
|
|
return JsonResponse(data, status=400)
|
|
|
|
|
else:
|
|
|
|
|
data = {'status': 'error', 'message': 'Worker not found'}
|
|
|
|
|
return JsonResponse(data, status=403)
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(dnsmasq_file):
|
|
|
|
|
data = {'status': 'error', 'message': 'dnsmasq configuration not found'}
|
|
|
|
|
return JsonResponse(data, status=404)
|
|
|
|
|
|
|
|
|
|
response = FileResponse(open(dnsmasq_file, "rb"), content_type="application/gzip")
|
|
|
|
|
response["Content-Disposition"] = 'attachment; filename="dnsmasq_config.tar.gz"'
|
|
|
|
|
response["Content-Length"] = str(os.path.getsize(dnsmasq_file))
|
|
|
|
|
return response
|
|
|
|
|
|
2026-01-08 13:43:51 -03:00
|
|
|
@csrf_exempt
|
2026-01-08 11:37:51 -03:00
|
|
|
def api_submit_worker_wireguard_stats(request):
|
|
|
|
|
worker, success = get_worker(request)
|
|
|
|
|
if worker:
|
|
|
|
|
if worker.error_status or not success:
|
|
|
|
|
data = {'status': 'error', 'message': worker.error_status}
|
|
|
|
|
return JsonResponse(data, status=400)
|
|
|
|
|
else:
|
|
|
|
|
data = {'status': 'error', 'message': 'Worker not found'}
|
|
|
|
|
return JsonResponse(data, status=403)
|
2026-01-08 13:43:51 -03:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
if request.method == 'POST':
|
|
|
|
|
payload = json.loads(request.body)
|
|
|
|
|
worker_status = worker.workerstatus
|
|
|
|
|
worker_status.wireguard_status = payload
|
|
|
|
|
worker_status.wireguard_status_updated = timezone.now()
|
|
|
|
|
worker_status.save()
|
|
|
|
|
data = {'status': 'success', 'message': 'Stats received'}
|
|
|
|
|
return JsonResponse(data, status=200)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
data = {'status': 'error', 'message': 'Stats not received'}
|
|
|
|
|
return JsonResponse(data, status=400)
|
2026-01-08 11:37:51 -03:00
|
|
|
|
|
|
|
|
|
2025-12-30 11:30:55 -03:00
|
|
|
def api_get_worker_config_files(request):
|
|
|
|
|
worker, success = get_worker(request)
|
|
|
|
|
if worker:
|
|
|
|
|
if worker.error_status or not success:
|
|
|
|
|
data = {'status': 'error', 'message': worker.error_status}
|
|
|
|
|
return JsonResponse(data, status=400)
|
|
|
|
|
else:
|
|
|
|
|
data = {'status': 'error', 'message': 'Worker not found'}
|
|
|
|
|
return JsonResponse(data, status=403)
|
|
|
|
|
|
|
|
|
|
config_files = (
|
|
|
|
|
glob.glob('/etc/wireguard/wg*.conf') +
|
|
|
|
|
glob.glob('/etc/wireguard/wg-firewall.sh')
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
files = {}
|
|
|
|
|
|
|
|
|
|
for path in config_files:
|
|
|
|
|
filename = os.path.basename(path)
|
|
|
|
|
with open(path, 'r') as f:
|
|
|
|
|
files[filename] = f.read()
|
2026-01-08 11:37:51 -03:00
|
|
|
|
|
|
|
|
data = {'status': 'success', 'files': files, 'cluster_settings': get_cluster_settings()}
|
|
|
|
|
return JsonResponse(data, status=200)
|
2025-12-30 11:30:55 -03:00
|
|
|
|
|
|
|
|
|
2025-12-31 20:27:22 -03:00
|
|
|
def api_worker_ping(request):
|
|
|
|
|
worker, success = get_worker(request)
|
|
|
|
|
if worker:
|
|
|
|
|
if worker.error_status or not success:
|
|
|
|
|
data = {'status': 'error', 'message': worker.error_status}
|
|
|
|
|
return JsonResponse(data, status=400)
|
|
|
|
|
else:
|
|
|
|
|
data = {'status': 'error', 'message': 'Worker not found'}
|
|
|
|
|
return JsonResponse(data, status=403)
|
|
|
|
|
|
|
|
|
|
data = {
|
|
|
|
|
'status': 'success',
|
|
|
|
|
'worker_error_status': worker.error_status,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return JsonResponse(data, status=200)
|
|
|
|
|
|
|
|
|
|
|
2025-12-30 11:30:55 -03:00
|
|
|
def api_cluster_status(request):
|
|
|
|
|
worker, success = get_worker(request)
|
|
|
|
|
if worker:
|
|
|
|
|
if worker.error_status or not success:
|
|
|
|
|
data = {'status': 'error', 'message': worker.error_status}
|
|
|
|
|
return JsonResponse(data, status=400)
|
|
|
|
|
else:
|
|
|
|
|
data = {'status': 'error', 'message': 'Worker not found'}
|
|
|
|
|
return JsonResponse(data, status=403)
|
2026-01-08 11:37:51 -03:00
|
|
|
data = {'status': 'success', 'worker_error_status': worker.error_status, 'cluster_settings': get_cluster_settings()}
|
2025-12-30 11:30:55 -03:00
|
|
|
return JsonResponse(data, status=200)
|