Scaffold Phase 0: Django project, deploy config, base layout
Django 5.2 with dev/test/prod settings, SQLite tuned for Litestream
(WAL, IMMEDIATE transactions), ULID primary keys via a shared base
model, and a custom User model ahead of the first migration.
Minimal accessible base template with dark mode via color-scheme and
an optional theme picker. /healthz answers before host validation so
Fly checks pass.
Dockerfile (dev and prod targets), compose for local dev, fly.toml
with placeholder app/region, Litestream backups to B2, and a Forgejo
Actions workflow. Migrations run in bin/start because Fly release
machines don't mount volumes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:59:34 -04:00
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
import sentry_sdk
|
|
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
|
|
|
|
|
|
|
|
|
from .base import * # noqa: F403
|
|
|
|
|
|
|
|
|
|
if not SECRET_KEY: # noqa: F405
|
|
|
|
|
raise ImproperlyConfigured("DJANGO_SECRET_KEY must be set")
|
2026-10-27 08:42:19 -04:00
|
|
|
# The console backend would print sign-in codes and invite links into the logs.
|
|
|
|
|
if EMAIL_BACKEND == "django.core.mail.backends.console.EmailBackend": # noqa: F405
|
|
|
|
|
raise ImproperlyConfigured("EMAIL_BACKEND must be set to a real email backend in production")
|
Scaffold Phase 0: Django project, deploy config, base layout
Django 5.2 with dev/test/prod settings, SQLite tuned for Litestream
(WAL, IMMEDIATE transactions), ULID primary keys via a shared base
model, and a custom User model ahead of the first migration.
Minimal accessible base template with dark mode via color-scheme and
an optional theme picker. /healthz answers before host validation so
Fly checks pass.
Dockerfile (dev and prod targets), compose for local dev, fly.toml
with placeholder app/region, Litestream backups to B2, and a Forgejo
Actions workflow. Migrations run in bin/start because Fly release
machines don't mount volumes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 06:59:34 -04:00
|
|
|
|
|
|
|
|
ALLOWED_HOSTS = [h for h in os.environ.get("DJANGO_ALLOWED_HOSTS", "").split(",") if h]
|
|
|
|
|
CSRF_TRUSTED_ORIGINS = [f"https://{h}" for h in ALLOWED_HOSTS]
|
|
|
|
|
|
|
|
|
|
# Fly terminates TLS and forwards the original scheme.
|
|
|
|
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|
|
|
|
SECURE_SSL_REDIRECT = True
|
|
|
|
|
SESSION_COOKIE_SECURE = True
|
|
|
|
|
CSRF_COOKIE_SECURE = True
|
|
|
|
|
# Short until the domain is settled; raise to a year after launch.
|
|
|
|
|
SECURE_HSTS_SECONDS = 60 * 60 * 24
|
|
|
|
|
|
|
|
|
|
MIDDLEWARE = [*MIDDLEWARE] # noqa: F405
|
|
|
|
|
MIDDLEWARE.insert(
|
|
|
|
|
MIDDLEWARE.index("django.middleware.security.SecurityMiddleware") + 1,
|
|
|
|
|
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
STORAGES = {
|
|
|
|
|
**STORAGES, # noqa: F405
|
|
|
|
|
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if dsn := os.environ.get("SENTRY_DSN"):
|
|
|
|
|
sentry_sdk.init(dsn=dsn, send_default_pii=False, traces_sample_rate=0)
|