From 059a404b837f1fd5a1770beb62c9f63a51e3f900 Mon Sep 17 00:00:00 2001 From: Maksxpl Date: Sat, 14 Feb 2026 20:18:55 +0100 Subject: [PATCH 1/4] ops --- .gitignore | 1 + nukeops/settings.py | 7 +++++++ nukeops/urls.py | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/.gitignore b/.gitignore index 0a77252..66c067a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ __pycache__/ !/media/sitemap.xml config.json *.log +/ops/* diff --git a/nukeops/settings.py b/nukeops/settings.py index f960c86..6ad3787 100644 --- a/nukeops/settings.py +++ b/nukeops/settings.py @@ -180,3 +180,10 @@ "compressor.finders.CompressorFinder", ] COMPRESS_ROOT = STATIC_ROOT if conf["static_path"] else "static/" + +try: + from ops.overwrite import app_overwrite + + INSTALLED_APPS = app_overwrite(INSTALLED_APPS) +except ImportError: + pass diff --git a/nukeops/urls.py b/nukeops/urls.py index 16ce29d..2e09b9c 100644 --- a/nukeops/urls.py +++ b/nukeops/urls.py @@ -14,6 +14,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.conf.urls import handler404, handler500 from django.contrib import admin from django.urls import include, path, re_path @@ -41,3 +42,10 @@ handler403 = error_403 handler404 = error_404 handler500 = error_500 + +try: + from ops.overwrite import urls_overwrite + + urlpatterns = urls_overwrite(urlpatterns) +except ImportError: + pass From 6c8a629e2e8f6236717978d5edf783cf4b39bc15 Mon Sep 17 00:00:00 2001 From: Maksxpl Date: Mon, 16 Feb 2026 00:00:11 +0100 Subject: [PATCH 2/4] err401 handling --- .gitignore | 2 ++ error_handlers/urls.py | 4 ++++ error_handlers/views.py | 11 +++++++++++ nukeops/urls.py | 5 +++-- 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 error_handlers/urls.py diff --git a/.gitignore b/.gitignore index 66c067a..f5fa29a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ __pycache__/ config.json *.log /ops/* +/static/ops/* + diff --git a/error_handlers/urls.py b/error_handlers/urls.py new file mode 100644 index 0000000..ec0ea75 --- /dev/null +++ b/error_handlers/urls.py @@ -0,0 +1,4 @@ +from django.urls import path +from . import views + +app_name = "error_handlers" diff --git a/error_handlers/views.py b/error_handlers/views.py index d44aef3..8b115b3 100644 --- a/error_handlers/views.py +++ b/error_handlers/views.py @@ -12,6 +12,17 @@ def error_400(request, exception): ) +def error_401(request, exception): + status_code = 401 + message = "UNAUTHORIZED" + return render( + request, + "error.html", + {"status_code": " ".join(str(status_code)), "message": message}, + status=status_code, + ) + + def error_403(request, exception): status_code = 403 message = "PERMISSION DENIED" diff --git a/nukeops/urls.py b/nukeops/urls.py index 2e09b9c..04ec1b7 100644 --- a/nukeops/urls.py +++ b/nukeops/urls.py @@ -15,13 +15,13 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.conf.urls import handler404, handler500 +# from django.conf.urls import handler404, handler500 from django.contrib import admin from django.urls import include, path, re_path from django.views.static import serve from auth_app.views import user_login -from error_handlers.views import error_400, error_403, error_404, error_500 +from error_handlers.views import error_400, error_401, error_403, error_404, error_500 from .settings import MEDIA_ROOT from .views import media_access @@ -39,6 +39,7 @@ ] handler400 = error_400 +handler401 = error_401 handler403 = error_403 handler404 = error_404 handler500 = error_500 From 58c905977f14f8d8264acbd6826ea0a15205ecc8 Mon Sep 17 00:00:00 2001 From: Maksxpl Date: Thu, 19 Feb 2026 18:11:20 +0100 Subject: [PATCH 3/4] auth fix what the **** I was smoking --- .gitignore | 1 + auth_app/templates/auth/login.html | 23 ++++++------------- auth_app/views.py | 37 +++++++++++++++++++++++++++--- nukeops/settings.py | 14 +++++++++++ 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index f5fa29a..a54f9ac 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ __pycache__/ .venv/ # app +/secrets.json /static/CACHE/ /media/* !/media/robots.txt diff --git a/auth_app/templates/auth/login.html b/auth_app/templates/auth/login.html index 5312da9..002b150 100644 --- a/auth_app/templates/auth/login.html +++ b/auth_app/templates/auth/login.html @@ -10,18 +10,6 @@ - -
@@ -33,8 +21,11 @@
{% csrf_token %} {{ form }} - -
-
- +
+
+ + + + + diff --git a/auth_app/views.py b/auth_app/views.py index a535f8e..64b242b 100644 --- a/auth_app/views.py +++ b/auth_app/views.py @@ -1,8 +1,25 @@ -from django.shortcuts import render, redirect -from django.contrib.auth import authenticate, login, logout +import requests +from django.conf import settings from django.contrib import messages +from django.contrib.auth import authenticate, login, logout +from django.shortcuts import redirect, render + from .forms import LoginForm +TURNSTILE_VERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify" + + +def verify_turnstile(token, ip=None): + data = { + "secret": settings.CLOUDFLARE_TURNSTILE_SECRET_KEY, + "response": token, + } + if ip: + data["remoteip"] = ip + + r = requests.post(TURNSTILE_VERIFY_URL, data=data, timeout=5) + return r.json() + def user_login(request): if request.user.is_authenticated: @@ -12,6 +29,16 @@ def user_login(request): form = LoginForm(initial={"next": initial_next}) if request.method == "POST": + # cf turnstile + token = request.POST.get("cf-turnstile-response") + if not token: + messages.error(request, "Captcha missing.") + return redirect("login") + result = verify_turnstile(token, request.META.get("REMOTE_ADDR")) + if not result.get("success"): + messages.error(request, "Captcha failed. Try again.") + + # auth form = LoginForm(request.POST) if form.is_valid(): username = form.cleaned_data["username"] @@ -24,7 +51,11 @@ def user_login(request): else: messages.error(request, "Invalid username or password.") - return render(request, "auth/login.html", {"form": form}) + return render( + request, + "auth/login.html", + {"form": form, "TURNSTILE_SITE_KEY": settings.CLOUDFLARE_TURNSTILE_SITE_KEY}, + ) def user_logout(request): diff --git a/nukeops/settings.py b/nukeops/settings.py index 6ad3787..8166903 100644 --- a/nukeops/settings.py +++ b/nukeops/settings.py @@ -187,3 +187,17 @@ INSTALLED_APPS = app_overwrite(INSTALLED_APPS) except ImportError: pass + +# Secrets +BASE_DIR = Path(__file__).resolve().parent.parent + +SECRETS_FILE = BASE_DIR / "secrets.json" + +if SECRETS_FILE.exists(): + with open(SECRETS_FILE) as f: + secrets = json.load(f) +else: + secrets = {} + +CLOUDFLARE_TURNSTILE_SITE_KEY = secrets.get("TURNSTILE_SITE_KEY", "") +CLOUDFLARE_TURNSTILE_SECRET_KEY = secrets.get("TURNSTILE_SECRET_KEY", "") From 47e6253522796848e48f7b20c5b71a2f734c2f74 Mon Sep 17 00:00:00 2001 From: Maksxpl Date: Thu, 19 Feb 2026 18:28:02 +0100 Subject: [PATCH 4/4] Update requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 4769ea8..8ac6da8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ wheel==0.42.0 channels==4.0.0 Django==5.0.2 +requests==2.32.5 mysqlclient==2.2.0 daphne==4.0.0 channels-redis==4.1.0