Coverage for sm / settings.py: 89%
70 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 12:43 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-24 12:43 +0000
1import os
2import dj_database_url
3from pathlib import Path
4from sys import platform, argv
5import django.contrib.messages as messages
6from decouple import config, Csv
8# Build paths inside the project like this: BASE_DIR / 'subdir'.
9BASE_DIR = Path(__file__).resolve().parent.parent
11# Environment-based configuration
12SECRET_KEY = config("SECRET_KEY", default="django-insecure-default-key-for-dev")
13DEBUG = config("DEBUG", default=True, cast=bool)
14ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="localhost,127.0.0.1", cast=Csv())
15EMAIL_BACKEND = config(
16 "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend"
17)
19THEME_CONTACT_EMAIL = config("THEME_CONTACT_EMAIL", default="oliver@linux-kernel.at")
20THEME_GITHUB_URL = config("THEME_GITHUB_URL", default="https://github.com/ofalk/sm")
22DISABLE_SOCIAL_AUTH = config("DISABLE_SOCIAL_AUTH", default=False, cast=bool)
24# Application definition
26INSTALLED_APPS = [
27 "django.contrib.admin",
28 "django.contrib.auth",
29 "django.contrib.contenttypes",
30 "django.contrib.sessions",
31 "django.contrib.messages",
32 "django.contrib.staticfiles",
33 "django.contrib.sites",
34 "django.contrib.admindocs",
35 "rest_framework",
36 "bootstrap4",
37 "debug_toolbar",
38 "django_countries",
39 "whitenoise.runserver_nostatic",
40 "taggit",
41 "simple_history",
42 "drf_spectacular",
43 # Allauth
44 "allauth",
45 "allauth.account",
46 "allauth.socialaccount",
47 "allauth.mfa",
48 "social_django",
49]
51if not DISABLE_SOCIAL_AUTH:
52 INSTALLED_APPS += [
53 # "allauth.socialaccount.providers.facebook",
54 "allauth.socialaccount.providers.google",
55 ]
56 SOCIALACCOUNT_ENABLED = True
57else:
58 SOCIALACCOUNT_ENABLED = False
60INSTALLED_APPS += [
61 # Project Apps
62 "cluster",
63 "operatingsystem",
64 "clusterpackage",
65 "patchtime",
66 "location",
67 "servermodel",
68 "server",
69 "status",
70 "domain",
71 "clustersoftware",
72 "clusterpackagetype",
73 "vendor",
74 "sm.apps.SmConfig",
75]
77MIDDLEWARE = [
78 "django.middleware.security.SecurityMiddleware",
79 "whitenoise.middleware.WhiteNoiseMiddleware",
80 "django.contrib.sessions.middleware.SessionMiddleware",
81 "django.middleware.common.CommonMiddleware",
82 "django.middleware.csrf.CsrfViewMiddleware",
83 "django.contrib.auth.middleware.AuthenticationMiddleware",
84 "django.contrib.messages.middleware.MessageMiddleware",
85 "django.middleware.clickjacking.XFrameOptionsMiddleware",
86 "debug_toolbar.middleware.DebugToolbarMiddleware",
87 "htmlmin.middleware.HtmlMinifyMiddleware",
88 "htmlmin.middleware.MarkRequestMiddleware",
89 "django.contrib.sites.middleware.CurrentSiteMiddleware",
90 "simple_history.middleware.HistoryRequestMiddleware",
91 # Allauth middleware
92 "allauth.account.middleware.AccountMiddleware",
93]
95ROOT_URLCONF = "sm.urls"
97TEMPLATES = [
98 {
99 "BACKEND": "django.template.backends.django.DjangoTemplates",
100 "DIRS": [BASE_DIR / "sm" / "templates"],
101 "OPTIONS": {
102 "context_processors": [
103 "django.template.context_processors.debug",
104 "django.template.context_processors.request",
105 "django.contrib.auth.context_processors.auth",
106 "django.contrib.messages.context_processors.messages",
107 "django.template.context_processors.i18n",
108 "django.template.context_processors.media",
109 "django.template.context_processors.static",
110 "django.template.context_processors.tz",
111 "sm.context_processors.theme_settings",
112 ],
113 "loaders": [
114 "django.template.loaders.filesystem.Loader",
115 "sm.template.loaders.app_directories_enhanced.Loader",
116 "django.template.loaders.app_directories.Loader",
117 ],
118 },
119 },
120]
122WSGI_APPLICATION = "sm.wsgi.application"
124# Database
125# https://docs.djangoproject.com/en/stable/ref/settings/#databases
127DATABASE_URL = config("DATABASE_URL", default=None)
129if DATABASE_URL:
130 DATABASES = {
131 "default": dj_database_url.config(default=DATABASE_URL, conn_max_age=600)
132 }
133elif "test" in argv:
134 DATABASES = {
135 "default": {
136 "ENGINE": "django.db.backends.sqlite3",
137 "NAME": ":memory:",
138 }
139 }
140elif platform == "darwin":
141 DATABASES = {
142 "default": {
143 "ENGINE": "django.db.backends.sqlite3",
144 "NAME": BASE_DIR / "db.sqlite3",
145 }
146 }
147else:
148 # Use config_local or environment variables for MySQL if needed
149 DATABASES = {
150 "default": {
151 "ENGINE": "django.db.backends.sqlite3",
152 "NAME": BASE_DIR / "db.sqlite3",
153 }
154 }
156# Password validation
157# https://docs.djangoproject.com/en/stable/ref/settings/#auth-password-validators
159AUTH_PASSWORD_VALIDATORS = [
160 {
161 "NAME": (
162 "django.contrib.auth.password_validation."
163 "UserAttributeSimilarityValidator"
164 )
165 },
166 {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
167 {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
168 {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
169]
171# Internationalization
172# https://docs.djangoproject.com/en/stable/topics/i18n/
174LANGUAGE_CODE = "en-us"
175TIME_ZONE = "UTC"
176USE_I18N = True
177USE_TZ = True
179# Static files (CSS, JavaScript, Images)
180# https://docs.djangoproject.com/en/stable/howto/static-files/
182STATIC_URL = "/static/"
183STATIC_ROOT = BASE_DIR / "static"
185# Default primary key field type
186# https://docs.djangoproject.com/en/stable/ref/settings/#default-auto-field
188DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
190# Extra settings
192SITE_ID = 1
194AUTHENTICATION_BACKENDS = [
195 # Needed to login by username in Django admin, regardless of `allauth`
196 "django.contrib.auth.backends.ModelBackend",
197 # `allauth` specific authentication methods, such as login by e-mail
198 "allauth.account.auth_backends.AuthenticationBackend",
199]
201# Allauth settings
202LOGIN_REDIRECT_URL = "/"
203ACCOUNT_LOGIN_METHODS = {"username", "email"}
204ACCOUNT_SIGNUP_FIELDS = ["email*", "username*", "password1*", "password2*"]
205ACCOUNT_EMAIL_VERIFICATION = "optional"
206ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = "/"
207ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = "/"
208ACCOUNT_LOGIN_REDIRECT_URL = "/"
209ACCOUNT_LOGOUT_REDIRECT_URL = "/"
211# Social Auth (Legacy/Transition)
212SOCIAL_AUTH_STRATEGY = "social_django.strategy.DjangoStrategy"
213SOCIAL_AUTH_STORAGE = "social_django.models.DjangoStorage"
215# Disable MFA features that might cause issues if allauth.mfa is not fully configured
216MFA_PASSKEY_LOGIN_ENABLED = False
217MFA_SUPPORTED_TYPES = ["totp", "recovery_codes"]
219if not DISABLE_SOCIAL_AUTH:
220 SOCIALACCOUNT_AUTO_SIGNUP = True
221 SOCIALACCOUNT_ADAPTER = "sm.adapter.MySocialAccountAdapter"
223 SOCIALACCOUNT_PROVIDERS = {
224 # "facebook": {
225 # "METHOD": "oauth2",
226 # "SCOPE": ["email", "public_profile"],
227 # "AUTH_PARAMS": {"auth_type": "reauthenticate"},
228 # "INIT_PARAMS": {"cookie": True},
229 # "FIELDS": [
230 # "id",
231 # "first_name",
232 # "last_name",
233 # "middle_name",
234 # "name",
235 # "name_format",
236 # "picture",
237 # "short_name",
238 # ],
239 # "EXCHANGE_TOKEN": True,
240 # "VERIFIED_EMAIL": False,
241 # "VERSION": "v13.0",
242 # },
243 "google": {
244 "SCOPE": [
245 "profile",
246 "email",
247 ],
248 "AUTH_PARAMS": {
249 "access_type": "online",
250 },
251 "AUTH_PKCE_ENABLED": True,
252 "FETCH_USERINFO": True,
253 }
254 }
256INTERNAL_IPS = [
257 "127.0.0.1",
258]
260MESSAGE_TAGS = {messages.ERROR: "danger"}
262MESSAGE_STORAGE = "django.contrib.messages.storage.fallback.FallbackStorage"
264TAGGIT_CASE_INSENSITIVE = True
266# REST Framework Settings
267REST_FRAMEWORK = {
268 "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
269}
271SPECTACULAR_SETTINGS = {
272 "TITLE": "ServerManager API",
273 "DESCRIPTION": "API for managing servers, clusters, and infrastructure.",
274 "VERSION": "1.0.0",
275 "SERVE_INCLUDE_SCHEMA": False,
276}
278# Bootstrap 4 settings
279BOOTSTRAP4 = {
280 "error_css_class": "bootstrap4-error",
281 "required_css_class": "bootstrap4-required",
282 "javascript_in_head": True,
283 "success_css_class": "bootstrap4-bound",
284}
286HTML_MINIFY = True
288if not DISABLE_SOCIAL_AUTH and os.path.isfile(BASE_DIR / "config_local.py"):
289 from config_local import * # noqa
291# Redefine to ensure no social_core backends survive in quick test mode
292if DISABLE_SOCIAL_AUTH:
293 AUTHENTICATION_BACKENDS = [
294 "django.contrib.auth.backends.ModelBackend",
295 "allauth.account.auth_backends.AuthenticationBackend",
296 ]