chore: deploy scripts + env

This commit is contained in:
2026-09-17 17:28:06 +02:00
parent 28d2a7ad24
commit 979be91417
8 changed files with 344 additions and 23 deletions
+102 -23
View File
@@ -13,23 +13,54 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
from pathlib import Path
import os
from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Optional: point this at a KEY=value file (see
# deploy/supervisor/polylan-submitter.env.dist for the template, and
# deploy/supervisor/polylan-submitter.conf for how it's wired up in
# production) to fill in the env vars read throughout this file. override
# =False so an already-exported env var always wins over the file - this
# only fills gaps, same as every os.environ.get() below already treating a
# merely-blank value as "use my own default" rather than "".
if os.environ.get("SETTINGS_ENV_FILE_LOCATION"):
load_dotenv(os.environ["SETTINGS_ENV_FILE_LOCATION"], override=False)
os.environ.setdefault("AWS_REQUEST_CHECKSUM_CALCULATION", "when_required")
os.environ.setdefault("AWS_RESPONSE_CHECKSUM_VALIDATION", "when_required")
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
#
# All of the settings below that read from os.environ fall back to
# quick-start/dev-friendly defaults when the env var is unset, EXCEPT
# DJANGO_DEBUG, which defaults to False (secure by default per Django's own
# deployment checklist) - export DJANGO_DEBUG=true for local development.
#
# These all use `os.environ.get(X) or default` rather than
# `os.environ.get(X, default)`: an env var that's *defined but blank* (e.g.
# systemd's EnvironmentFile represents "not configured" as `KEY=`, not by
# omitting the line) must fall through to the default too, not become "".
# That distinction matters a lot here - `int(os.environ.get("REDIS_PORT",
# "6379"))` would still crash on an empty string.
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-m0ivj_3gpbf281jl1$&n5pdo!5le(bp4z31u(1&4s=n#!tpy=n"
SECRET_KEY = (
os.environ.get("DJANGO_SECRET_KEY")
or "django-insecure-m0ivj_3gpbf281jl1$&n5pdo!5le(bp4z31u(1&4s=n#!tpy=n"
)
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
DEBUG = os.environ.get("DJANGO_DEBUG", "false").lower() == "true"
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
ALLOWED_HOSTS = [
host
for host in (os.environ.get("DJANGO_ALLOWED_HOSTS") or "127.0.0.1,localhost").split(",")
if host
]
# Application definition
@@ -58,6 +89,12 @@ INSTALLED_APPS = [
"place",
]
if DEBUG:
# Was unconditionally added by settingsLocal.py before it was retired in
# favor of env-var-driven settings; kept dev-only since it pulls in
# shell_plus and similar dev tooling that has no place in production.
INSTALLED_APPS += ["django_extensions"]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
@@ -95,13 +132,28 @@ ASGI_APPLICATION = "polylan_submitter.asgi.application"
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
#
# Falls back to sqlite (the quick-start default) when POSTGRES_DB isn't set,
# so a fresh checkout with no env configured still works out of the box.
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
if os.environ.get("POSTGRES_DB"):
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.environ["POSTGRES_DB"],
"USER": os.environ.get("POSTGRES_USER") or "postgres",
"PASSWORD": os.environ.get("POSTGRES_PASSWORD", ""),
"HOST": os.environ.get("POSTGRES_HOST") or "localhost",
"PORT": os.environ.get("POSTGRES_PORT") or "5432",
}
}
else:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
}
# Password validation
@@ -156,15 +208,15 @@ AUTH_USER_MODEL = "accounts.CustomUser"
CAS_SERVER_URL = "https://polylan.ch/cas/"
# Steam API Configuration
STEAM_API_KEY = os.environ.get("STEAM_API_KEY", None) # Set via environment variable
STEAM_API_KEY = os.environ.get("STEAM_API_KEY") or None # Set via environment variable
# Base URL of the legacy PolyLAN site, used to sync AAndT matches/members
# (see submissions.services.aandt_sync)
AANDT_API_BASE_URL = os.environ.get("AANDT_API_BASE_URL", "https://polylan.ch")
AANDT_API_BASE_URL = os.environ.get("AANDT_API_BASE_URL") or "https://polylan.ch"
# Base URL of this site, used to build submission links pushed back to the
# legacy AAndT match messages (see submissions.services.aandt_sync)
SUBMITTER_BASE_URL = os.environ.get("SUBMITTER_BASE_URL", "http://localhost:7777")
SUBMITTER_BASE_URL = os.environ.get("SUBMITTER_BASE_URL") or "http://localhost:7777"
# File Upload Settings
MEDIA_URL = "/media/"
@@ -174,11 +226,16 @@ MEDIA_ROOT = BASE_DIR / "media"
FILE_UPLOAD_MAX_MEMORY_SIZE = 256 * 1024 * 1024 # 256MB
DATA_UPLOAD_MAX_MEMORY_SIZE = 256 * 1024 * 1024 # 256MB
# Redis host/port shared by the cache, channel layer, RQ, and place's canvas
# below - each still uses its own DB number, just on a common host.
REDIS_HOST = os.environ.get("REDIS_HOST") or "127.0.0.1"
REDIS_PORT = int(os.environ.get("REDIS_PORT") or "6379")
# Caching Configuration
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/11",
"LOCATION": f"redis://{REDIS_HOST}:{REDIS_PORT}/11",
"TIMEOUT": 300, # 5 minutes default
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
@@ -191,7 +248,7 @@ CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("127.0.0.1", 6379)],
"hosts": [(REDIS_HOST, REDIS_PORT)],
"serializer_format": "cjson",
},
},
@@ -199,7 +256,7 @@ CHANNEL_LAYERS = {
# Redis for place's live pixel canvas (SETRANGE on the hot path - not a fit
# for django-redis's cache API, so it gets its own raw redis-py client).
PLACE_REDIS_URL = os.environ.get("PLACE_REDIS_URL", "redis://127.0.0.1:6379/15")
PLACE_REDIS_URL = os.environ.get("PLACE_REDIS_URL") or f"redis://{REDIS_HOST}:{REDIS_PORT}/15"
# Django Tasks Configuration (with RQ backend)
TASKS = {
@@ -210,8 +267,8 @@ TASKS = {
RQ_QUEUES = {
"default": {
"HOST": "127.0.0.1",
"PORT": 6379,
"HOST": REDIS_HOST,
"PORT": REDIS_PORT,
"DB": 12,
"DEFAULT_RESULT_TTL": 500,
}
@@ -228,7 +285,7 @@ ALLOWED_SUBMISSION_TYPES = [
]
# Market app settings
MARKET_ENABLED = os.environ.get("MARKET_ENABLED", "true").lower() == "true"
MARKET_ENABLED = (os.environ.get("MARKET_ENABLED") or "true").lower() == "true"
# Authentication backends
AUTHENTICATION_BACKENDS = [
@@ -243,7 +300,7 @@ LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"
DJANGO_VITE = {
"default": {
"dev_mode": False,
"dev_mode": DEBUG,
}
}
STATIC_URL = "/static/"
@@ -253,24 +310,46 @@ STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static_source/vite"),
]
# Submission/media uploads always go to S3 - there's no local fallback for
# that, so these must be set (via env) wherever the app runs, dev included.
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", "")
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", "")
AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL") or "https://storage.polylan.ch"
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME") or "dev-submissions"
_s3_storage_options = {
"bucket_name": AWS_STORAGE_BUCKET_NAME,
"endpoint_url": AWS_S3_ENDPOINT_URL,
"access_key": AWS_ACCESS_KEY_ID,
"secret_key": AWS_SECRET_ACCESS_KEY,
}
STORAGES = {
"default": {
"BACKEND": "storages.backends.s3.S3Storage",
"OPTIONS": _s3_storage_options,
},
# Static files are collected to local disk and served by WhiteNoise in
# DEBUG (no need to push every CSS/JS tweak to S3 while developing);
# production collects straight to S3 like everything else here.
"staticfiles": {
"BACKEND": "storages.backends.s3.S3Storage",
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"
if DEBUG
else "storages.backends.s3.S3Storage",
**({} if DEBUG else {"OPTIONS": _s3_storage_options}),
},
}
SENTRY_ENVIRONMENT = "production"
from polylan_submitter.settingsLocal import * # noqa
SENTRY_ENVIRONMENT = os.environ.get("SENTRY_ENVIRONMENT") or ("development" if DEBUG else "production")
SENTRY_DSN = (
os.environ.get("SENTRY_DSN") or "https://cc62a4ce3f3470890b43accf02cc6d8c@sentry2.polylan.ch/12"
)
import sentry_sdk # noqa
sentry_sdk.init(
dsn="https://cc62a4ce3f3470890b43accf02cc6d8c@sentry2.polylan.ch/12",
dsn=SENTRY_DSN,
# Set traces_sample_rate to 1.0 to capture 100%
# of transactions for tracing.
traces_sample_rate=1.0,