LAN admin on 10.0.0.128 plus quick campaign mint form (#12)
Deploy Beta / unit-tests (push) Successful in 4s
Deploy Beta / docker (push) Successful in 10s
Deploy Beta / deploy-beta (push) Successful in 53s

Closes #11.

## Summary
- Serve Django admin on LAN IP `10.0.0.128` (not `piha.li`). Compose now publishes `0.0.0.0:8005` and passes `SHORT_ADMIN_HOSTS` into the container.
- Admin index has a campaign mint form: domain, campaign, source, metric. Save builds `https://{domain}/?utm_campaign=&utm_source=&utm_medium=` (metric) and shows a copyable short URL.
- Public `/admin/` on `piha.lc` / `piha.li` stays 404.

## Test plan
- [ ] `cd site && uv run python manage.py test`
- [ ] Recreate compose (`docker compose up --build`) so `WEB_BIND` / `SHORT_ADMIN_HOSTS` take effect
- [ ] From another LAN machine: `http://10.0.0.128:8005/admin/` (staff login) shows the mint form
- [ ] Save a link for an allowlisted domain, copy the short URL, confirm it 302s
- [ ] `https://piha.li/admin` still 404

Reviewed-on: #12
This commit was merged in pull request #12.
This commit is contained in:
2026-09-16 03:39:04 -07:00
parent dd627b75c9
commit 69d8b3e7a3
10 changed files with 351 additions and 16 deletions
+56 -1
View File
@@ -1,6 +1,7 @@
from django import forms
from django.conf import settings
from links.services import ValidationError, validate_target_url
from links.services import ValidationError, build_tracked_url, validate_target_url
class DebugCreateForm(forms.Form):
@@ -19,3 +20,57 @@ class DebugCreateForm(forms.Form):
return validate_target_url(raw)
except ValidationError as exc:
raise forms.ValidationError(str(exc)) from exc
def allowlisted_domain_choices() -> list[str]:
hosts = list(getattr(settings, "SHORT_ALLOWED_HOSTS", []) or [])
return [host for host in hosts if host and not host.startswith("*")]
class QuickMintForm(forms.Form):
domain = forms.CharField(
label="Domain",
max_length=253,
widget=forms.TextInput(
attrs={
"placeholder": "mkdrealtor.com",
"list": "allowed-domains",
"autocomplete": "off",
}
),
)
campaign = forms.CharField(
label="Campaign",
max_length=200,
widget=forms.TextInput(attrs={"placeholder": "open-house"}),
)
source = forms.CharField(
label="Source",
max_length=200,
widget=forms.TextInput(attrs={"placeholder": "sms"}),
)
metric = forms.CharField(
label="Metric",
max_length=200,
widget=forms.TextInput(attrs={"placeholder": "listing-click"}),
help_text="Stored as utm_medium.",
)
def clean(self):
cleaned = super().clean()
domain = cleaned.get("domain")
campaign = cleaned.get("campaign")
source = cleaned.get("source")
metric = cleaned.get("metric")
if not all((domain, campaign, source, metric)):
return cleaned
try:
cleaned["target_url"] = build_tracked_url(
domain=domain,
campaign=campaign,
source=source,
metric=metric,
)
except ValidationError as exc:
self.add_error("domain", str(exc))
return cleaned