Files
stiftung-management-system/app/core/settings.py
SysAdmin Agent bf47ba11c9 Phase 1: Sidebar-Navigation, Dashboard-Cockpit & HTMX-Integration
- New sidebar layout (6 sections: Dashboard, Personen, Land, Finanzen, Dokumente, System)
- Collapsible sidebar with localStorage persistence
- Top bar with user dropdown and breadcrumbs
- Dashboard cockpit with live KPI cards (Destinataere, Foerderungen, Zahlungen, Laendereien)
- Action items: overdue Nachweise, pending payments, upcoming events, new emails, expiring leases
- Quick actions panel and recent audit log
- HTMX (2.0.4) and Alpine.js (3.14.8) integration via CDN
- django-htmx middleware and CSRF token setup
- Fix IMAP_PORT empty string handling in settings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:22:42 +00:00

186 lines
6.2 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",
]
# 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",
],
},
},
]
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("REDIS_URL", "redis://redis:6379/0")
CELERY_RESULT_BACKEND = os.getenv("REDIS_URL", "redis://redis:6379/0")
# Celery Beat periodische Tasks
from celery.schedules import crontab # noqa: E402
CELERY_BEAT_SCHEDULE = {
# E-Mail-Postfach alle 15 Minuten auf neue Destinatär-Nachrichten prüfen
"poll-destinataer-emails": {
"task": "stiftung.tasks.poll_destinataer_emails",
"schedule": crontab(minute="*/15"),
},
}
# 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"
# Paperless
PAPERLESS_API_URL = os.getenv("PAPERLESS_API_URL", "https://vhtv-stiftung.de/paperless")
PAPERLESS_API_TOKEN = os.getenv("PAPERLESS_API_TOKEN")
PAPERLESS_REQUIRED_TAG = os.getenv("PAPERLESS_REQUIRED_TAG", "Stiftung_Destinatäre")
PAPERLESS_LAND_TAG = os.getenv("PAPERLESS_LAND_TAG", "Stiftung_Land_und_Pächter")
PAPERLESS_ADMIN_TAG = os.getenv("PAPERLESS_ADMIN_TAG", "Stiftung_Administration")
PAPERLESS_DESTINATAERE_TAG_ID = os.getenv("PAPERLESS_DESTINATAERE_TAG_ID")
PAPERLESS_LAND_TAG_ID = os.getenv("PAPERLESS_LAND_TAG_ID")
PAPERLESS_ADMIN_TAG_ID = os.getenv("PAPERLESS_ADMIN_TAG_ID")
# Authentication
LOGIN_URL = "/login/"
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/login/"
# Gramps integration
GRAMPS_URL = os.environ.get("GRAMPS_URL", "http://grampsweb:80")
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