- REST API: 9 Read-Only-Endpunkte unter /api/v1/ für alle Kernmodelle (Destinatäre, Ländereien, Pächter, Förderungen, Konten, Verpachtungen, Verwaltungskosten, Kalender, Transaktionen) - Token-Authentifizierung via DRF TokenAuthentication - Management-Command `create_agent_token` für Agent-Tokens - Wissensbasis: knowledge/ mit Satzung, Richtlinien, Verfahren, Kontakte, Historie - Agent-Instructions: Datenzugriff-Sektion in AGENTS.md dokumentiert Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
183 lines
6.1 KiB
Python
183 lines
6.1 KiB
Python
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_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.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.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", "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 = '/two-factor/login/'
|
||
|
||
# Optional: Hide sensitive data in admin when not verified
|
||
OTP_ADMIN_HIDE_SENSITIVE_DATA = True
|