Files
training-software/waitlist/views.py
Paperclip DevOpsSRE 4b8622d197
Some checks failed
Deploy Waitlist / Deploy to VPS (push) Failing after 2m26s
feat(TRA-306): add waitlist app + VPS deploy workflow
Adds Django waitlist app to serve the launch-gating endpoints:
- GET  /training/django-cohort/waitlist  → HTML cohort waitlist page
- POST /v1/waitlist/django-cohort        → JSON signup with attribution

Changes:
- waitlist/ app: WaitlistSignup model, views, urls, admin, template
- config/settings/waitlist.py: minimal prod settings for waitlist-only deploy
- config/urls_waitlist.py: slimmed URL conf (waitlist + healthz + admin)
- config/urls.py: registers waitlist routes on full project
- config/settings/base.py: adds waitlist to INSTALLED_APPS
- docker-compose.waitlist.yml: Traefik-labelled deploy for VPS
- requirements/waitlist.txt: minimal dependency set for waitlist build
- .gitea/workflows/deploy-waitlist.yml: CI deploy job using Docker socket
- Dockerfile: parameterise DJANGO_SETTINGS_FOR_COLLECTSTATIC

DNS action required after deploy (board):
  api.usepaperclip.app  → 76.13.129.223 (VPS)
  usepaperclip.app      → 76.13.129.223 (VPS) OR Vercel creds for Next.js fix

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-07 10:17:25 +02:00

75 lines
2.3 KiB
Python

import json
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.http import JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .models import WaitlistSignup
ATTRIBUTION_KEYS = [
"utm_source",
"utm_medium",
"utm_campaign",
"utm_content",
"utm_term",
"utm_id",
"campaign_id",
"channel",
"variant",
"audience_segment",
"cta",
]
def cohort_waitlist(request):
return render(request, "waitlist/cohort_waitlist.html")
@csrf_exempt
@require_POST
def submit_cohort_waitlist(request):
payload = {}
try:
if request.content_type and "application/json" in request.content_type:
payload = json.loads((request.body or b"{}").decode("utf-8"))
else:
payload = request.POST.dict()
except (json.JSONDecodeError, UnicodeDecodeError):
return JsonResponse({"ok": False, "error": "invalid_json"}, status=400)
email = str(payload.get("email", "")).strip().lower()
name = str(payload.get("name", "")).strip()
source = str(payload.get("source", "cohort_waitlist_page")).strip()
if not email:
return JsonResponse({"ok": False, "error": "email_required"}, status=400)
try:
validate_email(email)
except ValidationError:
return JsonResponse({"ok": False, "error": "invalid_email"}, status=400)
attribution = {k: str(payload.get(k, "") or "").strip() for k in ATTRIBUTION_KEYS}
signup, created = WaitlistSignup.objects.get_or_create(
email=email,
defaults={"name": name, "source": source[:80], "attribution": attribution},
)
if not created:
fields_to_update = []
if name and signup.name != name:
signup.name = name
fields_to_update.append("name")
if source and signup.source != source[:80]:
signup.source = source[:80]
fields_to_update.append("source")
if signup.attribution != attribution:
signup.attribution = attribution
fields_to_update.append("attribution")
if fields_to_update:
signup.save(update_fields=fields_to_update)
return JsonResponse({"ok": True, "created": created})