Initial commit
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
"""
|
||||
Django settings for core project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 6.0.7.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.templatetags.static import static
|
||||
from django.urls import reverse, reverse_lazy
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def env_bool(name, default=False):
|
||||
return os.getenv(name, str(default)).strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
def env_list(name, default=""):
|
||||
return [value.strip() for value in os.getenv(name, default).split(",") if value.strip()]
|
||||
|
||||
|
||||
def load_local_env(path):
|
||||
"""Load a simple KEY=VALUE .env file without overriding server variables."""
|
||||
if not path.exists():
|
||||
return
|
||||
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||
|
||||
|
||||
load_local_env(BASE_DIR / ".env")
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
DEVELOPMENT_SECRET_KEY = 'django-insecure-development-only-change-me'
|
||||
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', DEVELOPMENT_SECRET_KEY)
|
||||
DEBUG = env_bool('DJANGO_DEBUG', True)
|
||||
ALLOWED_HOSTS = env_list('DJANGO_ALLOWED_HOSTS', '127.0.0.1,localhost')
|
||||
|
||||
if not DEBUG and SECRET_KEY == DEVELOPMENT_SECRET_KEY:
|
||||
raise ImproperlyConfigured("DJANGO_SECRET_KEY must be set to a secure value in production.")
|
||||
|
||||
# Application definition
|
||||
INSTALLED_APPS = [
|
||||
'unfold',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.sites',
|
||||
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
'allauth.socialaccount.providers.google',
|
||||
|
||||
'exams',
|
||||
'accounts.apps.AccountsConfig',
|
||||
]
|
||||
|
||||
# Keep the back office clean and close to Unfold's maintained defaults.
|
||||
UNFOLD = {
|
||||
"SITE_TITLE": "Testpoint Admin",
|
||||
"SITE_HEADER": "Testpoint",
|
||||
"SITE_ICON": lambda request: static("images/favicon.svg"),
|
||||
"SITE_SYMBOL": "menu_book",
|
||||
"SHOW_HISTORY": True,
|
||||
"SHOW_VIEW_ON_SITE": True,
|
||||
"DASHBOARD_CALLBACK": "core.admin_dashboard.dashboard_callback",
|
||||
"STYLES": [lambda request: "/static/css/unfold-dashboard.css"],
|
||||
"SIDEBAR": {
|
||||
"show_search": True,
|
||||
"show_all_applications": False,
|
||||
"navigation": [
|
||||
{
|
||||
"title": "Navigation",
|
||||
"separator": True,
|
||||
"items": [
|
||||
{"title": "Dashboard", "icon": "dashboard", "link": reverse_lazy("admin:index")},
|
||||
{"title": "Tests", "icon": "description", "link": reverse_lazy("admin:exams_examset_changelist")},
|
||||
{"title": "Questions", "icon": "quiz", "link": reverse_lazy("admin:exams_question_changelist")},
|
||||
{"title": "Results", "icon": "monitoring", "link": reverse_lazy("admin:exams_studentattempt_changelist")},
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "Create a test",
|
||||
"collapsible": True,
|
||||
"items": [
|
||||
{"title": "Reading test", "icon": "menu_book", "link": lambda request: reverse("admin:exams_examset_add") + "?category=reading"},
|
||||
{"title": "Listening test", "icon": "headphones", "link": lambda request: reverse("admin:exams_examset_add") + "?category=listening"},
|
||||
{"title": "Writing test", "icon": "edit_note", "link": lambda request: reverse("admin:exams_examset_add") + "?category=writing"},
|
||||
{"title": "Speaking test", "icon": "mic", "link": lambda request: reverse("admin:exams_examset_add") + "?category=speaking"},
|
||||
{"title": "Full Mock Test", "icon": "assignment", "link": lambda request: reverse("admin:exams_examset_add") + "?category=full"},
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "Import content",
|
||||
"collapsible": True,
|
||||
"items": [
|
||||
{
|
||||
"title": "Import Excel workbook",
|
||||
"icon": "upload_file",
|
||||
"link": reverse_lazy("admin:exams_examset_import_excel"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"title": "Users & settings",
|
||||
"separator": True,
|
||||
"items": [
|
||||
{"title": "Users", "icon": "group", "link": reverse_lazy("admin:auth_user_changelist")},
|
||||
{"title": "Student profiles", "icon": "school", "link": reverse_lazy("admin:accounts_studentprofile_changelist")},
|
||||
{"title": "Site settings", "icon": "settings", "link": reverse_lazy("admin:sites_site_changelist")},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
"COLORS": {"primary": {
|
||||
"50": "oklch(97.2% .016 251)", "100": "oklch(93.8% .043 251)",
|
||||
"200": "oklch(88.8% .086 251)", "300": "oklch(80.7% .14 251)",
|
||||
"400": "oklch(70.4% .177 251)", "500": "oklch(60.3% .195 251)",
|
||||
"600": "oklch(52.7% .19 251)", "700": "oklch(45.2% .16 251)",
|
||||
"800": "oklch(38.2% .127 251)", "900": "oklch(32.5% .091 251)",
|
||||
"950": "oklch(23.8% .057 251)"
|
||||
}},
|
||||
}
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
]
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'allauth.account.middleware.AccountMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
if not DEBUG:
|
||||
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
|
||||
|
||||
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.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
"core.context_processors.site_settings",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# Database
|
||||
DATABASE_URL = os.getenv('DATABASE_URL')
|
||||
if DATABASE_URL:
|
||||
import dj_database_url
|
||||
|
||||
database_ssl_default = not DEBUG and DATABASE_URL.startswith(
|
||||
("postgres://", "postgresql://")
|
||||
)
|
||||
DATABASES = {
|
||||
'default': dj_database_url.parse(
|
||||
DATABASE_URL,
|
||||
conn_max_age=600,
|
||||
conn_health_checks=True,
|
||||
ssl_require=env_bool('DJANGO_DB_SSL_REQUIRE', database_ssl_default),
|
||||
)
|
||||
}
|
||||
if DATABASES['default']['ENGINE'] == 'django.db.backends.mysql':
|
||||
mysql_options = DATABASES['default'].setdefault('OPTIONS', {})
|
||||
mysql_options.setdefault('charset', 'utf8mb4')
|
||||
mysql_options.setdefault('init_command', "SET sql_mode='STRICT_TRANS_TABLES'")
|
||||
else:
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
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'},
|
||||
]
|
||||
|
||||
# Internationalization
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
TIME_ZONE = 'Asia/Tashkent'
|
||||
USE_I18N = True
|
||||
USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Where collectstatic will put files
|
||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||
|
||||
# Extra places Django will look for static files (your app-level static folders)
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / "static",
|
||||
]
|
||||
|
||||
STORAGES = {
|
||||
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
|
||||
"staticfiles": {
|
||||
"BACKEND": (
|
||||
"django.contrib.staticfiles.storage.StaticFilesStorage"
|
||||
if DEBUG
|
||||
else "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
# Media files
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = BASE_DIR / 'media'
|
||||
DATA_UPLOAD_MAX_MEMORY_SIZE = 25 * 1024 * 1024
|
||||
FILE_UPLOAD_MAX_MEMORY_SIZE = 2_621_440
|
||||
|
||||
# Authentication
|
||||
LOGIN_REDIRECT_URL = '/accounts/dashboard/'
|
||||
ACCOUNT_LOGOUT_REDIRECT_URL = '/'
|
||||
|
||||
ACCOUNT_EMAIL_VERIFICATION = os.getenv(
|
||||
'DJANGO_ACCOUNT_EMAIL_VERIFICATION',
|
||||
'mandatory' if not DEBUG else 'none',
|
||||
)
|
||||
ACCOUNT_UNIQUE_EMAIL = True
|
||||
ACCOUNT_LOGIN_METHODS = {'email'}
|
||||
ACCOUNT_SIGNUP_FIELDS = ['email*', 'username*', 'password1*', 'password2*']
|
||||
|
||||
EMAIL_BACKEND = os.getenv(
|
||||
'DJANGO_EMAIL_BACKEND',
|
||||
(
|
||||
'django.core.mail.backends.console.EmailBackend'
|
||||
if DEBUG
|
||||
else 'django.core.mail.backends.smtp.EmailBackend'
|
||||
),
|
||||
)
|
||||
EMAIL_HOST = os.getenv('DJANGO_EMAIL_HOST', 'localhost')
|
||||
EMAIL_PORT = int(os.getenv('DJANGO_EMAIL_PORT', '587'))
|
||||
EMAIL_HOST_USER = os.getenv('DJANGO_EMAIL_HOST_USER', '')
|
||||
EMAIL_HOST_PASSWORD = os.getenv('DJANGO_EMAIL_HOST_PASSWORD', '')
|
||||
EMAIL_USE_TLS = env_bool('DJANGO_EMAIL_USE_TLS', True)
|
||||
DEFAULT_FROM_EMAIL = os.getenv('DJANGO_DEFAULT_FROM_EMAIL', 'Testpoint <noreply@localhost>')
|
||||
SERVER_EMAIL = os.getenv('DJANGO_SERVER_EMAIL', DEFAULT_FROM_EMAIL)
|
||||
SUPPORT_EMAIL = os.getenv('DJANGO_SUPPORT_EMAIL', 'support@ieltsmock.com')
|
||||
SOCIAL_FACEBOOK_URL = os.getenv('SOCIAL_FACEBOOK_URL', '')
|
||||
SOCIAL_INSTAGRAM_URL = os.getenv('SOCIAL_INSTAGRAM_URL', '')
|
||||
SOCIAL_YOUTUBE_URL = os.getenv('SOCIAL_YOUTUBE_URL', '')
|
||||
SOCIAL_TELEGRAM_URL = os.getenv('SOCIAL_TELEGRAM_URL', '')
|
||||
|
||||
SOCIALACCOUNT_PROVIDERS = {
|
||||
'google': {
|
||||
'SCOPE': ['profile', 'email'],
|
||||
'AUTH_PARAMS': {'access_type': 'online'},
|
||||
}
|
||||
}
|
||||
|
||||
# Production security. These remain development-friendly while DEBUG=True.
|
||||
CSRF_TRUSTED_ORIGINS = env_list('DJANGO_CSRF_TRUSTED_ORIGINS')
|
||||
SECURE_SSL_REDIRECT = env_bool('DJANGO_SECURE_SSL_REDIRECT', not DEBUG)
|
||||
SESSION_COOKIE_SECURE = not DEBUG
|
||||
CSRF_COOKIE_SECURE = not DEBUG
|
||||
SECURE_HSTS_SECONDS = int(os.getenv('DJANGO_SECURE_HSTS_SECONDS', '3600' if not DEBUG else '0'))
|
||||
SECURE_HSTS_INCLUDE_SUBDOMAINS = env_bool('DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', False)
|
||||
SECURE_HSTS_PRELOAD = env_bool('DJANGO_SECURE_HSTS_PRELOAD', False)
|
||||
SECURE_CONTENT_TYPE_NOSNIFF = True
|
||||
X_FRAME_OPTIONS = 'DENY'
|
||||
|
||||
if env_bool('DJANGO_TRUST_PROXY_HEADERS', False):
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
|
||||
LOG_LEVEL = os.getenv('DJANGO_LOG_LEVEL', 'INFO')
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format': '{asctime} {levelname} {name}: {message}',
|
||||
'style': '{',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'standard',
|
||||
},
|
||||
},
|
||||
'root': {'handlers': ['console'], 'level': LOG_LEVEL},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['console'],
|
||||
'level': 'WARNING',
|
||||
'propagate': False,
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user