refactor: update route for downloader to apps/downloader chore: remove unused filler model files refactor: delete Default layout component and its imports
954 lines
33 KiB
Python
954 lines
33 KiB
Python
"""
|
||
Django settings for vontor_cz project.
|
||
|
||
Generated by 'django-admin startproject' using Django 5.1.3.
|
||
|
||
For more information on this file, see
|
||
https://docs.djangoproject.com/en/5.1/topics/settings/
|
||
|
||
For the full list of settings and their values, see
|
||
https://docs.djangoproject.com/en/5.1/ref/settings/
|
||
"""
|
||
import os
|
||
from typing import Dict, Any
|
||
from pathlib import Path
|
||
|
||
from django.core.management.utils import get_random_secret_key
|
||
from django.db import OperationalError, connections
|
||
|
||
from datetime import timedelta
|
||
import json
|
||
|
||
from dotenv import load_dotenv
|
||
load_dotenv() # Pouze načte proměnné lokálně, pokud nejsou dostupné
|
||
|
||
# Robust boolean parser and SSL flag
|
||
def _env_bool(key: str, default: bool = False) -> bool:
|
||
return os.getenv(key, str(default)).strip().lower() in ("true", "1", "yes", "on")
|
||
|
||
USE_SSL = _env_bool("SSL", False)
|
||
|
||
#---------------- ENV VARIABLES USECASE--------------
|
||
# v jiné app si to importneš skrz: from django.conf import settings
|
||
# a použiješ takto: settings.FRONTEND_URL
|
||
|
||
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:9000")
|
||
print(f"FRONTEND_URL: {FRONTEND_URL}\n")
|
||
#-------------------------BASE ⚙️------------------------
|
||
|
||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||
|
||
|
||
# Pavel
|
||
# from django.conf.locale.en import formats as en_formats
|
||
|
||
DATETIME_INPUT_FORMATS = [
|
||
"%Y-%m-%d", # '2025-07-25'
|
||
"%Y-%m-%d %H:%M", # '2025-07-25 14:30'
|
||
"%Y-%m-%d %H:%M:%S", # '2025-07-25 14:30:59'
|
||
"%Y-%m-%dT%H:%M", # '2025-07-25T14:30'
|
||
"%Y-%m-%dT%H:%M:%S", # '2025-07-25T14:30:59'
|
||
]
|
||
|
||
# -------------------- LOKALIZACE -------------------------
|
||
|
||
LANGUAGE_CODE = os.getenv("LANGUAGE_CODE", "cs")
|
||
TIME_ZONE = os.getenv("TIME_ZONE", "Europe/Prague")
|
||
|
||
USE_I18N = True
|
||
USE_L10N = True
|
||
USE_TZ = True
|
||
|
||
|
||
|
||
|
||
# SECURITY WARNING: don't run with debug turned on in production!
|
||
if os.getenv("DEBUG", "") == "True":
|
||
DEBUG = True
|
||
else:
|
||
DEBUG = False
|
||
|
||
print(f"\nDEBUG state: {str(DEBUG)}\nDEBUG .env raw: {os.getenv('DEBUG', '')}\n")
|
||
|
||
#-----------------------BASE END⚙️--------------------------
|
||
|
||
#--------------- URLS 🌐 -------------------
|
||
|
||
ASGI_APPLICATION = 'vontor_cz.asgi.application' #daphne
|
||
ROOT_URLCONF = 'vontor_cz.urls'
|
||
LOGIN_URL = '/admin' #nastavení Login adresy
|
||
|
||
#-----------------------------------------
|
||
|
||
|
||
|
||
#----------------------------------- LOGS -------------------------------------------
|
||
#slouží pro tisknutí do konzole v dockeru skrz: logger.debug("content")
|
||
LOGGING = {
|
||
"version": 1,
|
||
"disable_existing_loggers": False,
|
||
"handlers": {
|
||
"console": {
|
||
"class": "logging.StreamHandler",
|
||
"formatter": "verbose",
|
||
},
|
||
},
|
||
"formatters": {
|
||
"verbose": {
|
||
"format": "{levelname} {asctime} {name}: {message}",
|
||
"style": "{",
|
||
},
|
||
},
|
||
"root": {
|
||
"handlers": ["console"],
|
||
"level": "DEBUG" if DEBUG else "INFO",
|
||
},
|
||
}
|
||
|
||
# -- PŘÍKLAD POUŽITÍ LOGS --
|
||
"""
|
||
import logging
|
||
|
||
# Vytvoř si logger podle názvu souboru (modulu)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
logger.debug("Ladicí zpráva – vidíš jen když je DEBUG = True")
|
||
logger.info("Informace – např. že uživatel klikl na tlačítko")
|
||
logger.warning("Varování – něco nečekaného, ale ne kritického")
|
||
logger.error("Chyba – něco se pokazilo, ale aplikace jede dál")
|
||
logger.critical("Kritická chyba – selhání systému, třeba pád služby")
|
||
"""
|
||
|
||
# -- SILK --
|
||
SILKY_PYTHON_PROFILER = True
|
||
|
||
#---------------------------------- END LOGS ---------------------------------------
|
||
|
||
#-------------------------------------SECURITY 🔐------------------------------------
|
||
|
||
if DEBUG:
|
||
SECRET_KEY = 'pernament'
|
||
else:
|
||
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", get_random_secret_key())
|
||
|
||
|
||
SESSION_COOKIE_AGE = 86400 # one day
|
||
|
||
|
||
AUTH_PASSWORD_VALIDATORS = [
|
||
{
|
||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||
},
|
||
{
|
||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||
},
|
||
{
|
||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||
},
|
||
{
|
||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||
},
|
||
]
|
||
|
||
AUTHENTICATION_BACKENDS = [
|
||
#'vontor_cz.backend.EmailOrUsernameModelBackend', #custom backend z authentication aplikace
|
||
'django.contrib.auth.backends.ModelBackend',
|
||
]
|
||
|
||
#--------------------------------END SECURITY 🔐-------------------------------------
|
||
|
||
#-------------------------------------CORS + HOSTs 🌐🔐------------------------------------
|
||
|
||
ALLOWED_HOSTS = ["*"]
|
||
|
||
from urllib.parse import urlparse
|
||
parsed = urlparse(FRONTEND_URL)
|
||
|
||
CSRF_TRUSTED_ORIGINS = [
|
||
f"{parsed.scheme}://{parsed.hostname}:{parsed.port or (443 if parsed.scheme=='https' else 80)}",
|
||
|
||
"http://192.168.67.98",
|
||
"https://itsolutions.vontor.cz",
|
||
"https://react.vontor.cz",
|
||
|
||
"http://localhost:5173",
|
||
"http://localhost:3000",
|
||
"http://localhost:9000",
|
||
|
||
"http://127.0.0.1:5173",
|
||
"http://127.0.0.1:3000",
|
||
"http://127.0.0.1:9000",
|
||
|
||
# server
|
||
"http://192.168.67.98",
|
||
"https://itsolutions.vontor.cz",
|
||
"https://react.vontor.cz",
|
||
|
||
# nginx docker (local)
|
||
"http://localhost",
|
||
"http://localhost:80",
|
||
"http://127.0.0.1",
|
||
]
|
||
|
||
if DEBUG:
|
||
CORS_ALLOWED_ORIGINS = [
|
||
f"{parsed.scheme}://{parsed.hostname}:{parsed.port or (443 if parsed.scheme=='https' else 80)}",
|
||
|
||
"http://localhost:5173",
|
||
"http://localhost:3000",
|
||
"http://127.0.0.1:5173",
|
||
"http://127.0.0.1:3000",
|
||
"http://localhost:9000",
|
||
"http://127.0.0.1:9000",
|
||
|
||
# server
|
||
"http://192.168.67.98",
|
||
"https://itsolutions.vontor.cz",
|
||
"https://react.vontor.cz",
|
||
|
||
# nginx docker (local)
|
||
"http://localhost",
|
||
"http://localhost:80",
|
||
"http://127.0.0.1",
|
||
]
|
||
else:
|
||
CORS_ALLOWED_ORIGINS = [
|
||
"http://192.168.67.98",
|
||
"https://itsolutions.vontor.cz",
|
||
"https://react.vontor.cz",
|
||
|
||
"http://localhost:9000",
|
||
"http://127.0.0.1:9000",
|
||
]
|
||
|
||
CORS_ALLOW_CREDENTIALS = True
|
||
CORS_ALLOW_ALL_ORIGINS = False # Tohle musí být false, když používáš credentials
|
||
|
||
# Use Lax for http (local), None only when HTTPS is enabled
|
||
SESSION_COOKIE_SAMESITE = "None" if USE_SSL else "Lax"
|
||
CSRF_COOKIE_SAMESITE = "None" if USE_SSL else "Lax"
|
||
|
||
print("CORS_ALLOWED_ORIGINS =", CORS_ALLOWED_ORIGINS)
|
||
print("CSRF_TRUSTED_ORIGINS =", CSRF_TRUSTED_ORIGINS)
|
||
print("ALLOWED_HOSTS =", ALLOWED_HOSTS)
|
||
|
||
#--------------------------------END CORS + HOSTs 🌐🔐---------------------------------
|
||
|
||
|
||
#--------------------------------------SSL 🧾------------------------------------
|
||
if USE_SSL is True:
|
||
print("SSL turned on!")
|
||
SESSION_COOKIE_SECURE = True
|
||
CSRF_COOKIE_SECURE = True
|
||
SECURE_SSL_REDIRECT = True
|
||
SECURE_BROWSER_XSS_FILTER = True
|
||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||
else:
|
||
SESSION_COOKIE_SECURE = False
|
||
CSRF_COOKIE_SECURE = False
|
||
SECURE_SSL_REDIRECT = False
|
||
SECURE_BROWSER_XSS_FILTER = False
|
||
SECURE_CONTENT_TYPE_NOSNIFF = False
|
||
print(f"\nUsing SSL: {USE_SSL}\n")
|
||
#--------------------------------END-SSL 🧾---------------------------------
|
||
|
||
|
||
|
||
|
||
|
||
#-------------------------------------REST FRAMEWORK 🛠️------------------------------------
|
||
|
||
# ⬇️ Základní lifetime konfigurace
|
||
ACCESS_TOKEN_LIFETIME = timedelta(minutes=60)
|
||
REFRESH_TOKEN_LIFETIME = timedelta(days=5)
|
||
|
||
# ⬇️ Nastavení SIMPLE_JWT podle režimu
|
||
if DEBUG:
|
||
SIMPLE_JWT = {
|
||
"ACCESS_TOKEN_LIFETIME": ACCESS_TOKEN_LIFETIME,
|
||
"REFRESH_TOKEN_LIFETIME": REFRESH_TOKEN_LIFETIME,
|
||
|
||
"AUTH_COOKIE": "access_token",
|
||
"AUTH_COOKIE_REFRESH": "refresh_token",
|
||
|
||
"AUTH_COOKIE_DOMAIN": None,
|
||
"AUTH_COOKIE_SECURE": False,
|
||
"AUTH_COOKIE_HTTP_ONLY": True,
|
||
"AUTH_COOKIE_PATH": "/",
|
||
"AUTH_COOKIE_SAMESITE": "Lax",
|
||
|
||
"ROTATE_REFRESH_TOKENS": False,
|
||
"BLACKLIST_AFTER_ROTATION": False,
|
||
}
|
||
else:
|
||
SIMPLE_JWT = {
|
||
"ACCESS_TOKEN_LIFETIME": ACCESS_TOKEN_LIFETIME,
|
||
"REFRESH_TOKEN_LIFETIME": REFRESH_TOKEN_LIFETIME,
|
||
|
||
"AUTH_COOKIE": "access_token",
|
||
"AUTH_COOKIE_REFRESH": "refresh_token",
|
||
"AUTH_COOKIE_DOMAIN": None,
|
||
|
||
# Secure/SameSite based on HTTPS availability
|
||
"AUTH_COOKIE_SECURE": USE_SSL,
|
||
"AUTH_COOKIE_HTTP_ONLY": True,
|
||
"AUTH_COOKIE_PATH": "/",
|
||
"AUTH_COOKIE_SAMESITE": "None" if USE_SSL else "Lax",
|
||
|
||
"ROTATE_REFRESH_TOKENS": True,
|
||
"BLACKLIST_AFTER_ROTATION": True,
|
||
}
|
||
|
||
REST_FRAMEWORK = {
|
||
"DATETIME_FORMAT": "%Y-%m-%d %H:%M", # Pavel
|
||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||
# In DEBUG keep Session + JWT + your cookie class for convenience
|
||
'rest_framework.authentication.SessionAuthentication',
|
||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||
'account.tokens.CookieJWTAuthentication',
|
||
) if DEBUG else (
|
||
'account.tokens.CookieJWTAuthentication',
|
||
),
|
||
'DEFAULT_PERMISSION_CLASSES': (
|
||
'rest_framework.permissions.AllowAny',
|
||
),
|
||
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
|
||
|
||
# Enable default pagination so custom list actions (e.g., /orders/detail) paginate
|
||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
||
'PAGE_SIZE': 20,
|
||
|
||
'DEFAULT_THROTTLE_RATES': {
|
||
'anon': '100/hour', # unauthenticated
|
||
'user': '2000/hour', # authenticated
|
||
}
|
||
}
|
||
#--------------------------------END REST FRAMEWORK 🛠️-------------------------------------
|
||
|
||
|
||
|
||
#-------------------------------------APPS 📦------------------------------------
|
||
MY_CREATED_APPS = [
|
||
'account',
|
||
'commerce',
|
||
'configuration',
|
||
|
||
'social.chat',
|
||
|
||
'advertisement',
|
||
|
||
'thirdparty.downloader',
|
||
'thirdparty.stripe', # register Stripe app so its models are recognized
|
||
'thirdparty.trading212',
|
||
'thirdparty.zasilkovna',
|
||
'thirdparty.gopay', # add GoPay app
|
||
]
|
||
|
||
INSTALLED_APPS = [
|
||
'daphne', #asgi bude fungovat lokálně (musí být na začátku)
|
||
|
||
'django.contrib.admin',
|
||
'django.contrib.auth',
|
||
'django.contrib.contenttypes',
|
||
'django.contrib.sessions',
|
||
'django.contrib.messages',
|
||
'django.contrib.staticfiles',
|
||
|
||
'corsheaders', #cors
|
||
|
||
'django_celery_beat', #slouží k plánování úkolů pro Celery
|
||
|
||
|
||
#'chat.apps.GlobalChatCheck', #tohle se spusti při každé django inicializaci (migration, createmigration, runserver)
|
||
|
||
#'authentication',
|
||
|
||
'storages',# Adds support for external storage services like Amazon S3 via django-storages
|
||
'django_filters',
|
||
|
||
'channels' ,# django channels
|
||
|
||
'rest_framework',
|
||
'rest_framework_api_key',
|
||
|
||
'drf_spectacular', #rest framework, grafické zobrazení
|
||
|
||
#Nastavení stránky
|
||
#'constance',
|
||
#'constance.backends.database',
|
||
|
||
'silk',
|
||
|
||
'django.contrib.sitemaps',
|
||
|
||
'tinymce',
|
||
|
||
|
||
#kvůli bugum je lepší to dát na poslední místo v INSTALLED_APPS
|
||
'django_cleanup.apps.CleanupConfig', #app která maže nepoužité soubory(media) z databáze na S3
|
||
]
|
||
|
||
#skládaní dohromady INSTALLED_APPS
|
||
INSTALLED_APPS = INSTALLED_APPS[:-1] + MY_CREATED_APPS + INSTALLED_APPS[-1:]
|
||
|
||
# -------------------------------------END APPS 📦------------------------------------
|
||
|
||
|
||
|
||
|
||
|
||
#-------------------------------------MIDDLEWARE 🧩------------------------------------
|
||
# Middleware is a framework of hooks into Django's request/response processing.
|
||
|
||
MIDDLEWARE = [
|
||
"corsheaders.middleware.CorsMiddleware",
|
||
"django.middleware.common.CommonMiddleware",
|
||
'django.middleware.security.SecurityMiddleware',
|
||
|
||
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||
|
||
'silk.middleware.SilkyMiddleware',
|
||
|
||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||
'django.middleware.csrf.CsrfViewMiddleware',
|
||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||
'django.contrib.messages.middleware.MessageMiddleware',
|
||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||
]
|
||
|
||
#--------------------------------END MIDDLEWARE 🧩---------------------------------
|
||
|
||
|
||
|
||
|
||
|
||
#-------------------------------------CACHE + CHANNELS(ws) 📡🗄️------------------------------------
|
||
|
||
# Caching settings for Redis (using Docker's internal network name for Redis)
|
||
if DEBUG is False:
|
||
#PRODUCTION
|
||
CACHES = {
|
||
'default': {
|
||
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
||
'LOCATION': 'redis://redis:6379/0', # Using the service name `redis` from Docker Compose
|
||
'OPTIONS': {
|
||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||
'PASSWORD': os.getenv('REDIS_PASSWORD'), # Make sure to set REDIS_PASSWORD in your environment
|
||
},
|
||
}
|
||
}
|
||
|
||
# WebSockets Channel Layers (using Redis in production)
|
||
CHANNEL_LAYERS = {
|
||
'default': {
|
||
'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
||
'CONFIG': {
|
||
'hosts': [('redis', 6379)], # Use `redis` service in Docker Compose
|
||
},
|
||
}
|
||
}
|
||
|
||
else:
|
||
#DEVELOPMENT
|
||
# Use in-memory channel layer for development (when DEBUG is True)
|
||
CHANNEL_LAYERS = {
|
||
'default': {
|
||
'BACKEND': 'channels.layers.InMemoryChannelLayer',
|
||
}
|
||
}
|
||
|
||
# Use in-memory cache for development (when DEBUG is True)
|
||
CACHES = {
|
||
'default': {
|
||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||
}
|
||
}
|
||
|
||
#--------------------------------END CACHE + CHANNELS(ws) 📡🗄️---------------------------------
|
||
|
||
#-------------------------------------CELERY 📅------------------------------------
|
||
CELERY_BROKER_URL = os.getenv("CELERY_BROKER_URL")
|
||
CELERY_RESULT_BACKEND = os.getenv("CELERY_RESULT_BACKEND")
|
||
# Control via env; default False in DEBUG, True otherwise
|
||
CELERY_ENABLED = _env_bool("CELERY_ENABLED", default=not DEBUG)
|
||
|
||
if DEBUG:
|
||
CELERY_ENABLED = False
|
||
|
||
try:
|
||
import redis
|
||
r = redis.Redis(host='localhost', port=6379, db=0)
|
||
r.ping()
|
||
except Exception:
|
||
CELERY_BROKER_URL = 'memory://'
|
||
CELERY_ENABLED = False
|
||
|
||
def _env_list(key: str, default: list[str]) -> list[str]:
|
||
v = os.getenv(key)
|
||
if not v:
|
||
return default
|
||
try:
|
||
parsed = json.loads(v)
|
||
if isinstance(parsed, (list, tuple)):
|
||
return list(parsed)
|
||
if isinstance(parsed, str):
|
||
return [parsed]
|
||
except Exception:
|
||
pass
|
||
return [s.strip(" '\"") for s in v.strip("[]()").split(",") if s.strip()]
|
||
|
||
CELERY_ACCEPT_CONTENT = _env_list("CELERY_ACCEPT_CONTENT", ["json"])
|
||
CELERY_RESULT_ACCEPT_CONTENT = _env_list("CELERY_RESULT_ACCEPT_CONTENT", ["json"])
|
||
CELERY_TASK_SERIALIZER = os.getenv("CELERY_TASK_SERIALIZER", "json")
|
||
CELERY_RESULT_SERIALIZER = os.getenv("CELERY_RESULT_SERIALIZER", "json")
|
||
CELERY_TIMEZONE = os.getenv("CELERY_TIMEZONE", TIME_ZONE)
|
||
CELERY_BEAT_SCHEDULER = os.getenv("CELERY_BEAT_SCHEDULER")
|
||
#-------------------------------------END CELERY 📅------------------------------------
|
||
|
||
|
||
#-------------------------------------DATABASE 💾------------------------------------
|
||
|
||
# Nastavuje výchozí typ primárního klíče pro modely.
|
||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||
|
||
# říka že se úkladá do databáze, místo do cookie
|
||
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
|
||
|
||
USE_DOCKER_DB = os.getenv("USE_DOCKER_DB", "False") in ["True", "true", "1", True]
|
||
|
||
if USE_DOCKER_DB is False:
|
||
# DEV
|
||
DATABASES = {
|
||
'default': {
|
||
'ENGINE': 'django.db.backends.sqlite3',
|
||
'NAME': BASE_DIR / 'db.sqlite3',
|
||
}
|
||
}
|
||
else:
|
||
# DOCKER/POSTGRES
|
||
DATABASES = {
|
||
'default': {
|
||
'ENGINE': os.getenv('DATABASE_ENGINE'),
|
||
'NAME': os.getenv('POSTGRES_DB'),
|
||
'USER': os.getenv('POSTGRES_USER'),
|
||
'PASSWORD': os.getenv('POSTGRES_PASSWORD'),
|
||
'HOST': os.getenv('DATABASE_HOST'),
|
||
'PORT': os.getenv('DATABASE_PORT'),
|
||
}
|
||
}
|
||
|
||
print(f"\nUsing Docker DB: {USE_DOCKER_DB}\nDatabase settings: {DATABASES}\n")
|
||
AUTH_USER_MODEL = 'account.CustomUser' #class CustomUser(AbstractUser) best practice to use AbstractUser
|
||
|
||
#--------------------------------END DATABASE 💾---------------------------------
|
||
|
||
#--------------------------------------EMAIL 📧--------------------------------------
|
||
|
||
EMAIL_BACKEND = os.getenv(
|
||
"EMAIL_BACKEND",
|
||
'django.core.mail.backends.console.EmailBackend' if DEBUG else 'django.core.mail.backends.smtp.EmailBackend'
|
||
)
|
||
|
||
EMAIL_HOST = os.getenv("EMAIL_HOST")
|
||
EMAIL_PORT = int(os.getenv("EMAIL_PORT", 465))
|
||
EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", "False") in ["True", "true", "1", True]
|
||
EMAIL_USE_SSL = os.getenv("EMAIL_USE_SSL", "True") in ["True", "true", "1", True]
|
||
EMAIL_HOST_USER = os.getenv("EMAIL_USER")
|
||
EMAIL_HOST_PASSWORD = os.getenv("EMAIL_USER_PASSWORD")
|
||
DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", EMAIL_HOST_USER)
|
||
EMAIL_TIMEOUT = 30 # seconds
|
||
|
||
print("---------EMAIL----------")
|
||
print("EMAIL_HOST =", EMAIL_HOST)
|
||
print("EMAIL_PORT =", EMAIL_PORT)
|
||
print("EMAIL_USE_TLS =", EMAIL_USE_TLS)
|
||
print("EMAIL_USE_SSL =", EMAIL_USE_SSL)
|
||
print("EMAIL_USER =", EMAIL_HOST_USER)
|
||
print("------------------------")
|
||
#----------------------------------EMAIL END 📧-------------------------------------
|
||
|
||
|
||
|
||
|
||
#-------------------------------------TEMPLATES 🗂️------------------------------------
|
||
|
||
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',
|
||
],
|
||
},
|
||
},
|
||
]
|
||
|
||
#--------------------------------END TEMPLATES 🗂️---------------------------------
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
#-------------------------------------MEDIA + STATIC 🖼️, AWS ☁️------------------------------------
|
||
|
||
# nastavení složky pro globalstaticfiles (static složky django hledá samo)
|
||
STATICFILES_DIRS = [
|
||
BASE_DIR / 'globalstaticfiles',
|
||
]
|
||
|
||
|
||
|
||
if os.getenv("USE_AWS", "") == "True":
|
||
USE_AWS = True
|
||
else:
|
||
USE_AWS = False
|
||
|
||
print(f"\n-------------- USE_AWS: {USE_AWS} --------------")
|
||
|
||
if USE_AWS is False:
|
||
# Development: Use local file system storage for static files
|
||
STORAGES = {
|
||
"default": {
|
||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||
},
|
||
"staticfiles": {
|
||
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
||
},
|
||
}
|
||
|
||
# Media and Static URL for local dev
|
||
MEDIA_URL = os.getenv("MEDIA_URL", "/media/")
|
||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||
|
||
STATIC_URL = '/static/'
|
||
STATIC_ROOT = BASE_DIR / 'collectedstaticfiles'
|
||
elif USE_AWS:
|
||
# PRODUCTION
|
||
|
||
AWS_LOCATION = "static"
|
||
|
||
# Production: Use S3 storage
|
||
STORAGES = {
|
||
"default": {
|
||
"BACKEND" : "storages.backends.s3boto3.S3StaticStorage",
|
||
},
|
||
|
||
"staticfiles": {
|
||
"BACKEND" : "storages.backends.s3boto3.S3StaticStorage",
|
||
},
|
||
}
|
||
|
||
# Media and Static URL for AWS S3
|
||
MEDIA_URL = f'https://{os.getenv("AWS_STORAGE_BUCKET_NAME")}.s3.amazonaws.com/media/'
|
||
STATIC_URL = f'https://{os.getenv("AWS_STORAGE_BUCKET_NAME")}.s3.amazonaws.com/static/'
|
||
|
||
CSRF_TRUSTED_ORIGINS.append(STATIC_URL)
|
||
|
||
# Static files should be collected to a local directory and then uploaded to S3
|
||
STATIC_ROOT = BASE_DIR / 'collectedstaticfiles'
|
||
|
||
AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID')
|
||
AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY')
|
||
AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME')
|
||
AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME', 'us-east-1') # Default to 'us-east-1' if not set
|
||
AWS_S3_SIGNATURE_VERSION = 's3v4' # Use AWS Signature Version 4
|
||
AWS_S3_USE_SSL = True
|
||
AWS_S3_FILE_OVERWRITE = True
|
||
AWS_DEFAULT_ACL = None # Set to None to avoid setting a default ACL
|
||
|
||
|
||
|
||
print(f"Static url: {STATIC_URL}\nStatic storage: {STORAGES}\n----------------------------")
|
||
#--------------------------------END: MEDIA + STATIC 🖼️, AWS ☁️---------------------------------
|
||
|
||
|
||
|
||
#-------------------------------------TINY MCE ✍️------------------------------------
|
||
|
||
TINYMCE_JS_URL = 'https://cdn.tiny.cloud/1/no-api-key/tinymce/7/tinymce.min.js'
|
||
|
||
TINYMCE_DEFAULT_CONFIG = {
|
||
"height": "320px",
|
||
"width": "960px",
|
||
"menubar": "file edit view insert format tools table help",
|
||
"plugins": "advlist autolink lists link image charmap print preview anchor searchreplace visualblocks code "
|
||
"fullscreen insertdatetime media table paste code help wordcount spellchecker",
|
||
"toolbar": "undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft "
|
||
"aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | forecolor "
|
||
"backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | "
|
||
"fullscreen preview save print | insertfile image media pageembed template link anchor codesample | "
|
||
"a11ycheck ltr rtl | showcomments addcomment code",
|
||
"custom_undo_redo_levels": 10,
|
||
}
|
||
TINYMCE_SPELLCHECKER = True
|
||
TINYMCE_COMPRESSOR = True
|
||
|
||
#--------------------------------END-TINY-MCE-SECTION ✍️---------------------------------
|
||
|
||
|
||
|
||
#-------------------------------------DRF SPECTACULAR 📊------------------------------------
|
||
|
||
SPECTACULAR_DEFAULTS: Dict[str, Any] = {
|
||
# A regex specifying the common denominator for all operation paths. If
|
||
# SCHEMA_PATH_PREFIX is set to None, drf-spectacular will attempt to estimate
|
||
# a common prefix. Use '' to disable.
|
||
# Mainly used for tag extraction, where paths like '/api/v1/albums' with
|
||
# a SCHEMA_PATH_PREFIX regex '/api/v[0-9]' would yield the tag 'albums'.
|
||
'SCHEMA_PATH_PREFIX': None,
|
||
|
||
# Remove matching SCHEMA_PATH_PREFIX from operation path. Usually used in
|
||
# conjunction with appended prefixes in SERVERS.
|
||
'SCHEMA_PATH_PREFIX_TRIM': False,
|
||
|
||
# Insert a manual path prefix to the operation path, e.g. '/service/backend'.
|
||
# Use this for example to align paths when the API is mounted as a sub-resource
|
||
# behind a proxy and Django is not aware of that. Alternatively, prefixes can
|
||
# also specified via SERVERS, but this makes the operation path more explicit.
|
||
'SCHEMA_PATH_PREFIX_INSERT': '',
|
||
|
||
# Coercion of {pk} to {id} is controlled by SCHEMA_COERCE_PATH_PK. Additionally,
|
||
# some libraries (e.g. drf-nested-routers) use "_pk" suffixed path variables.
|
||
# This setting globally coerces path variables like "{user_pk}" to "{user_id}".
|
||
'SCHEMA_COERCE_PATH_PK_SUFFIX': False,
|
||
|
||
# Schema generation parameters to influence how components are constructed.
|
||
# Some schema features might not translate well to your target.
|
||
# Demultiplexing/modifying components might help alleviate those issues.
|
||
'DEFAULT_GENERATOR_CLASS': 'drf_spectacular.generators.SchemaGenerator',
|
||
|
||
# Create separate components for PATCH endpoints (without required list)
|
||
'COMPONENT_SPLIT_PATCH': True,
|
||
|
||
# Split components into request and response parts where appropriate
|
||
# This setting is highly recommended to achieve the most accurate API
|
||
# description, however it comes at the cost of having more components.
|
||
'COMPONENT_SPLIT_REQUEST': True,
|
||
|
||
# Aid client generator targets that have trouble with read-only properties.
|
||
'COMPONENT_NO_READ_ONLY_REQUIRED': False,
|
||
|
||
# Adds "minLength: 1" to fields that do not allow blank strings. Deactivated
|
||
# by default because serializers do not strictly enforce this on responses and
|
||
# so "minLength: 1" may not always accurately describe API behavior.
|
||
# Gets implicitly enabled by COMPONENT_SPLIT_REQUEST, because this can be
|
||
# accurately modeled when request and response components are separated.
|
||
'ENFORCE_NON_BLANK_FIELDS': False,
|
||
|
||
# This version string will end up the in schema header. The default OpenAPI
|
||
# version is 3.0.3, which is heavily tested. We now also support 3.1.0,
|
||
# which contains the same features and a few mandatory, but minor changes.
|
||
'OAS_VERSION': '3.0.3',
|
||
|
||
# Configuration for serving a schema subset with SpectacularAPIView
|
||
'SERVE_URLCONF': None,
|
||
|
||
# complete public schema or a subset based on the requesting user
|
||
'SERVE_PUBLIC': True,
|
||
|
||
# include schema endpoint into schema
|
||
'SERVE_INCLUDE_SCHEMA': True,
|
||
|
||
# list of authentication/permission classes for spectacular's views.
|
||
'SERVE_PERMISSIONS': ['rest_framework.permissions.AllowAny'], #account.permissions.AdminOnly
|
||
|
||
# None will default to DRF's AUTHENTICATION_CLASSES
|
||
'SERVE_AUTHENTICATION': None,
|
||
|
||
# Dictionary of general configuration to pass to the SwaggerUI({ ... })
|
||
# https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
|
||
# The settings are serialized with json.dumps(). If you need customized JS, use a
|
||
# string instead. The string must then contain valid JS and is passed unchanged.
|
||
'SWAGGER_UI_SETTINGS': {
|
||
'deepLinking': True,
|
||
},
|
||
|
||
# Initialize SwaggerUI with additional OAuth2 configuration.
|
||
# https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/
|
||
'SWAGGER_UI_OAUTH2_CONFIG': {},
|
||
|
||
# Dictionary of general configuration to pass to the Redoc.init({ ... })
|
||
# https://redocly.com/docs/redoc/config/#functional-settings
|
||
# The settings are serialized with json.dumps(). If you need customized JS, use a
|
||
# string instead. The string must then contain valid JS and is passed unchanged.
|
||
'REDOC_UI_SETTINGS': {},
|
||
|
||
# CDNs for swagger and redoc. You can change the version or even host your
|
||
# own depending on your requirements. For self-hosting, have a look at
|
||
# the sidecar option in the README.
|
||
'SWAGGER_UI_DIST': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest',
|
||
'SWAGGER_UI_FAVICON_HREF': 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@latest/favicon-32x32.png',
|
||
'REDOC_DIST': 'https://cdn.jsdelivr.net/npm/redoc@latest',
|
||
|
||
# Append OpenAPI objects to path and components in addition to the generated objects
|
||
'APPEND_PATHS': {},
|
||
'APPEND_COMPONENTS': {},
|
||
|
||
|
||
# Postprocessing functions that run at the end of schema generation.
|
||
# must satisfy interface result = hook(generator, request, public, result)
|
||
'POSTPROCESSING_HOOKS': [
|
||
'drf_spectacular.hooks.postprocess_schema_enums'
|
||
],
|
||
|
||
# Preprocessing functions that run before schema generation.
|
||
# must satisfy interface result = hook(endpoints=result) where result
|
||
# is a list of Tuples (path, path_regex, method, callback).
|
||
# Example: 'drf_spectacular.hooks.preprocess_exclude_path_format'
|
||
'PREPROCESSING_HOOKS': [],
|
||
|
||
# Determines how operations should be sorted. If you intend to do sorting with a
|
||
# PREPROCESSING_HOOKS, be sure to disable this setting. If configured, the sorting
|
||
# is applied after the PREPROCESSING_HOOKS. Accepts either
|
||
# True (drf-spectacular's alpha-sorter), False, or a callable for sort's key arg.
|
||
'SORT_OPERATIONS': True,
|
||
|
||
# enum name overrides. dict with keys "YourEnum" and their choice values "field.choices"
|
||
# e.g. {'SomeEnum': ['A', 'B'], 'OtherEnum': 'import.path.to.choices'}
|
||
'ENUM_NAME_OVERRIDES': {
|
||
'OrderStatusEnum': 'commerce.models.Order.OrderStatus.choices',
|
||
},
|
||
|
||
# Adds "blank" and "null" enum choices where appropriate. disable on client generation issues
|
||
'ENUM_ADD_EXPLICIT_BLANK_NULL_CHOICE': True,
|
||
|
||
# Add/Append a list of (``choice value`` - choice name) to the enum description string.
|
||
'ENUM_GENERATE_CHOICE_DESCRIPTION': True,
|
||
|
||
# Optional suffix for generated enum.
|
||
# e.g. {'ENUM_SUFFIX': "Type"} would produce an enum name 'StatusType'.
|
||
'ENUM_SUFFIX': 'Enum',
|
||
|
||
# function that returns a list of all classes that should be excluded from doc string extraction
|
||
'GET_LIB_DOC_EXCLUDES': 'drf_spectacular.plumbing.get_lib_doc_excludes',
|
||
|
||
# Function that returns a mocked request for view processing. For CLI usage
|
||
# original_request will be None.
|
||
# interface: request = build_mock_request(method, path, view, original_request, **kwargs)
|
||
'GET_MOCK_REQUEST': 'drf_spectacular.plumbing.build_mock_request',
|
||
|
||
# Camelize names like "operationId" and path parameter names
|
||
# Camelization of the operation schema itself requires the addition of
|
||
# 'drf_spectacular.contrib.djangorestframework_camel_case.camelize_serializer_fields'
|
||
# to POSTPROCESSING_HOOKS. Please note that the hook depends on
|
||
# ``djangorestframework_camel_case``, while CAMELIZE_NAMES itself does not.
|
||
'CAMELIZE_NAMES': False,
|
||
|
||
# Changes the location of the action/method on the generated OperationId. For example,
|
||
# "POST": "group_person_list", "group_person_create"
|
||
# "PRE": "list_group_person", "create_group_person"
|
||
'OPERATION_ID_METHOD_POSITION': 'POST',
|
||
|
||
# Determines if and how free-form 'additionalProperties' should be emitted in the schema. Some
|
||
# code generator targets are sensitive to this. None disables generic 'additionalProperties'.
|
||
# allowed values are 'dict', 'bool', None
|
||
'GENERIC_ADDITIONAL_PROPERTIES': 'dict',
|
||
|
||
# Path converter schema overrides (e.g. <int:foo>). Can be used to either modify default
|
||
# behavior or provide a schema for custom converters registered with register_converter(...).
|
||
# Takes converter labels as keys and either basic python types, OpenApiType, or raw schemas
|
||
# as values. Example: {'aint': OpenApiTypes.INT, 'bint': str, 'cint': {'type': ...}}
|
||
'PATH_CONVERTER_OVERRIDES': {},
|
||
|
||
# Determines whether operation parameters should be sorted alphanumerically or just in
|
||
# the order they arrived. Accepts either True, False, or a callable for sort's key arg.
|
||
'SORT_OPERATION_PARAMETERS': True,
|
||
|
||
# @extend_schema allows to specify status codes besides 200. This functionality is usually used
|
||
# to describe error responses, which rarely make use of list mechanics. Therefore, we suppress
|
||
# listing (pagination and filtering) on non-2XX status codes by default. Toggle this to enable
|
||
# list responses with ListSerializers/many=True irrespective of the status code.
|
||
'ENABLE_LIST_MECHANICS_ON_NON_2XX': False,
|
||
|
||
# This setting allows you to deviate from the default manager by accessing a different model
|
||
# property. We use "objects" by default for compatibility reasons. Using "_default_manager"
|
||
# will likely fix most issues, though you are free to choose any name.
|
||
"DEFAULT_QUERY_MANAGER": 'objects',
|
||
|
||
# Controls which authentication methods are exposed in the schema. If not None, will hide
|
||
# authentication classes that are not contained in the whitelist. Use full import paths
|
||
# like ['rest_framework.authentication.TokenAuthentication', ...].
|
||
# Empty list ([]) will hide all authentication methods. The default None will show all.
|
||
'AUTHENTICATION_WHITELIST': None,
|
||
# Controls which parsers are exposed in the schema. Works analog to AUTHENTICATION_WHITELIST.
|
||
# List of allowed parsers or None to allow all.
|
||
'PARSER_WHITELIST': None,
|
||
# Controls which renderers are exposed in the schema. Works analog to AUTHENTICATION_WHITELIST.
|
||
# rest_framework.renderers.BrowsableAPIRenderer is ignored by default if whitelist is None
|
||
'RENDERER_WHITELIST': None,
|
||
|
||
# Option for turning off error and warn messages
|
||
'DISABLE_ERRORS_AND_WARNINGS': False,
|
||
|
||
# Runs exemplary schema generation and emits warnings as part of "./manage.py check --deploy"
|
||
'ENABLE_DJANGO_DEPLOY_CHECK': True,
|
||
|
||
# General schema metadata. Refer to spec for valid inputs
|
||
# https://spec.openapis.org/oas/v3.0.3#openapi-object
|
||
'TITLE': 'e-Tržnice API',
|
||
'DESCRIPTION': 'This is the API documentation for e-Tržnice.',
|
||
'TOS': None,
|
||
# Optional: MAY contain "name", "url", "email"
|
||
'CONTACT': {},
|
||
# Optional: MUST contain "name", MAY contain URL
|
||
|
||
'LICENSE': {},
|
||
# Statically set schema version. May also be an empty string. When used together with
|
||
# view versioning, will become '0.0.0 (v2)' for 'v2' versioned requests.
|
||
# Set VERSION to None if only the request version should be rendered.
|
||
'VERSION': '1.0.0',
|
||
# Optional list of servers.
|
||
# Each entry MUST contain "url", MAY contain "description", "variables"
|
||
# e.g. [{'url': 'https://example.com/v1', 'description': 'Text'}, ...]
|
||
'SERVERS': [],
|
||
# Tags defined in the global scope
|
||
'TAGS': [],
|
||
# Optional: List of OpenAPI 3.1 webhooks. Each entry should be an import path to an
|
||
# OpenApiWebhook instance.
|
||
'WEBHOOKS': [],
|
||
# Optional: MUST contain 'url', may contain "description"
|
||
'EXTERNAL_DOCS': {},
|
||
|
||
# Arbitrary specification extensions attached to the schema's info object.
|
||
# https://swagger.io/specification/#specification-extensions
|
||
'EXTENSIONS_INFO': {},
|
||
|
||
# Arbitrary specification extensions attached to the schema's root object.
|
||
# https://swagger.io/specification/#specification-extensions
|
||
'EXTENSIONS_ROOT': {},
|
||
|
||
# Oauth2 related settings. used for example by django-oauth2-toolkit.
|
||
# https://spec.openapis.org/oas/v3.0.3#oauth-flows-object
|
||
'OAUTH2_FLOWS': [],
|
||
'OAUTH2_AUTHORIZATION_URL': None,
|
||
'OAUTH2_TOKEN_URL': None,
|
||
'OAUTH2_REFRESH_URL': None,
|
||
'OAUTH2_SCOPES': None,
|
||
}
|
||
|
||
# --- GoPay configuration (set in backend/.env) ---
|
||
GOPAY_GOID = os.getenv("GOPAY_GOID")
|
||
GOPAY_CLIENT_ID = os.getenv("GOPAY_CLIENT_ID")
|
||
GOPAY_CLIENT_SECRET = os.getenv("GOPAY_CLIENT_SECRET")
|
||
GOPAY_GATEWAY_URL = os.getenv("GOPAY_GATEWAY_URL", "https://gw.sandbox.gopay.com/api")
|
||
# New: absolute URL that GoPay calls (publicly reachable)
|
||
GOPAY_NOTIFICATION_URL = os.getenv("GOPAY_NOTIFICATION_URL", "http://localhost:8000/api/payments/gopay/webhook")
|
||
|
||
# -------------------------------------DOWNLOADER LIMITS------------------------------------
|
||
DOWNLOADER_MAX_SIZE_MB = int(os.getenv("DOWNLOADER_MAX_SIZE_MB", "200")) # Raspberry Pi safe cap
|
||
DOWNLOADER_MAX_SIZE_BYTES = DOWNLOADER_MAX_SIZE_MB * 1024 * 1024
|
||
DOWNLOADER_TIMEOUT = int(os.getenv("DOWNLOADER_TIMEOUT", "120")) # seconds
|
||
DOWNLOADER_TMP_DIR = os.getenv("DOWNLOADER_TMP_DIR", str(BASE_DIR / "tmp" / "downloader"))
|
||
# -------------------------------------END DOWNLOADER LIMITS--------------------------------
|