Files
stiftung-management-system/app/core/settings.py
SysAdmin Agent f893172a2b GrampsWeb Phase 1: Sidebar-Link, Settings-Fix & Env-Template (STI-90)
- Fix GRAMPS_URL default port from 80 to 5000 to match dev compose
- Add "Ahnenforschung" sidebar link in navigation (links to /ahnenforschung/)
- Update env-template with all GRAMPSWEB_* variables for production setup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 23:40:55 +00:00

197 lines
6.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
from pathlib import Path
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.getenv("SECRET_KEY") or os.getenv("DJANGO_SECRET_KEY", "dev-secret")
DEBUG = os.getenv("DJANGO_DEBUG", "0") == "1"
# Support both ALLOWED_HOSTS (production) and DJANGO_ALLOWED_HOSTS (development)
allowed_hosts_env = os.getenv("ALLOWED_HOSTS") or os.getenv("DJANGO_ALLOWED_HOSTS", "*")
ALLOWED_HOSTS = [host.strip() for host in allowed_hosts_env.split(",")]
# CSRF settings for development and production
CSRF_TRUSTED_ORIGINS = [
"http://localhost:8081",
"http://127.0.0.1:8081",
"http://vhtv-stiftung.de",
"https://vhtv-stiftung.de",
"http://www.vhtv-stiftung.de",
"https://www.vhtv-stiftung.de",
]
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.humanize",
"rest_framework",
"rest_framework.authtoken",
"django_htmx",
"django_otp",
"django_otp.plugins.otp_totp",
"django_otp.plugins.otp_static",
"stiftung",
"django.contrib.postgres",
]
# Add this to app/core/settings.py
SESSION_COOKIE_NAME = 'stiftung_sessionid' # Different from default 'sessionid'
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django_htmx.middleware.HtmxMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_otp.middleware.OTPMiddleware",
"stiftung.middleware.TwoFactorMiddleware", # 2FA enforcement middleware
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"stiftung.middleware.AuditMiddleware", # Audit logging middleware
]
ROOT_URLCONF = "core.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"core.context_processors.app_version",
],
},
},
]
WSGI_APPLICATION = "core.wsgi.application"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": os.getenv("POSTGRES_DB", "stiftung"),
"USER": os.getenv("POSTGRES_USER", "stiftung"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD", "stiftungpass"),
"HOST": os.getenv("DB_HOST", "db"),
"PORT": os.getenv("DB_PORT", "5432"),
}
}
LANGUAGE_CODE = os.getenv("LANGUAGE_CODE", "de")
TIME_ZONE = os.getenv("TIME_ZONE", "Europe/Berlin")
USE_I18N = True
USE_TZ = True
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
# Additional locations of static files
STATICFILES_DIRS = [
BASE_DIR / "static",
]
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
}
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
# Celery
CELERY_BROKER_URL = os.getenv("CELERY_REDIS_URL", os.getenv("REDIS_URL", "redis://redis:6379/2"))
CELERY_RESULT_BACKEND = os.getenv("CELERY_REDIS_URL", os.getenv("REDIS_URL", "redis://redis:6379/2"))
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_ACCEPT_CONTENT = ["json"]
# Celery Beat periodische Tasks
from celery.schedules import crontab # noqa: E402
CELERY_BEAT_SCHEDULE = {
# E-Mail-Postfach alle 15 Minuten auf neue Nachrichten pruefen
"poll-emails": {
"task": "stiftung.tasks.poll_emails",
"schedule": crontab(minute="*/15"),
},
# Täglich um 08:00 Uhr: Ablaufende Upload-Tokens prüfen und Erinnerungen versenden
"check-ablaufende-tokens": {
"task": "stiftung.tasks.check_ablaufende_tokens",
"schedule": crontab(hour="8", minute="0"),
},
}
# IMAP-Konfiguration für E-Mail-Eingang (Destinatäre)
# Pflichtfelder: IMAP_HOST, IMAP_USER, IMAP_PASSWORD
IMAP_HOST = os.getenv("IMAP_HOST", "")
IMAP_PORT = int(os.getenv("IMAP_PORT") or "993")
IMAP_USER = os.getenv("IMAP_USER", "paperless@vhtv-stiftung.de")
IMAP_PASSWORD = os.getenv("IMAP_PASSWORD", "")
IMAP_FOLDER = os.getenv("IMAP_FOLDER", "INBOX")
IMAP_USE_SSL = os.getenv("IMAP_USE_SSL", "true").lower() == "true"
# SMTP-Konfiguration für E-Mail-Ausgang (Nachweis-Aufforderungen, Einladungen)
# Pflichtfelder: EMAIL_HOST_USER, EMAIL_HOST_PASSWORD
EMAIL_HOST = os.getenv("EMAIL_HOST", "smtp.ionos.de")
EMAIL_PORT = int(os.getenv("EMAIL_PORT") or "465")
EMAIL_USE_SSL = os.getenv("EMAIL_USE_SSL", "true").lower() == "true"
EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", "")
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", "")
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "stiftung@vhtv-stiftung.de")
EMAIL_SUBJECT_PREFIX = "[vHTV-Stiftung] "
# Authentication
LOGIN_URL = "/login/"
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/login/"
# Gramps integration
GRAMPS_URL = os.environ.get("GRAMPS_URL", "http://grampsweb:5000")
GRAMPS_API_TOKEN = os.environ.get("GRAMPS_API_TOKEN", "")
GRAMPS_STIFTER_IDS = os.environ.get("GRAMPS_STIFTER_IDS", "") # comma-separated
GRAMPS_USERNAME = os.environ.get("GRAMPS_USERNAME", "")
GRAMPS_PASSWORD = os.environ.get("GRAMPS_PASSWORD", "")
# HTTPS Security Settings (production)
if not DEBUG:
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# =============================================================================
# TWO-FACTOR AUTHENTICATION SETTINGS
# =============================================================================
# django-otp settings
OTP_TOTP_ISSUER = 'Stiftung Management System'
OTP_LOGIN_URL = '/auth/2fa/verify/'
# Optional: Hide sensitive data in admin when not verified
OTP_ADMIN_HIDE_SENSITIVE_DATA = True