Coverage for config\settings\base.py: 75%
81 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-10-17 16:50 +0800
« prev ^ index » next coverage.py v7.16.0, created at 2026-10-17 16:50 +0800
1import os
2from pathlib import Path
3import dj_database_url
4from dotenv import load_dotenv
6load_dotenv()
8BASE_DIR = Path(__file__).resolve().parent.parent.parent
10SECRET_KEY = os.getenv('SECRET_KEY', 'django-insecure-fudo-erp-dev-key-change-in-production')
11DEBUG = os.getenv('DEBUG', 'True') == 'True'
12ALLOWED_HOSTS = [host.strip() for host in os.getenv('ALLOWED_HOSTS', 'localhost,127.0.0.1,*').split(',')]
14# Application definition
15DJANGO_APPS = [
16 'daphne', # Must be before django.contrib.staticfiles
17 'django.contrib.admin',
18 'django.contrib.auth',
19 'django.contrib.contenttypes',
20 'django.contrib.sessions',
21 'django.contrib.messages',
22 'django.contrib.staticfiles',
23]
25THIRD_PARTY_APPS = [
26 'corsheaders',
27 'channels',
28]
30LOCAL_APPS = [
31 'core',
32 'modules.authentication.apps.AuthenticationConfig',
33 'modules.team.apps.TeamConfig',
34 'modules.crm.apps.CrmConfig',
35 'modules.erp.apps.ErpConfig',
36 'modules.planner.apps.PlannerConfig',
37 'modules.chat.apps.ChatConfig',
38 'modules.notifications.apps.NotificationsConfig',
39 'modules.hr.apps.HrConfig',
40 'modules.email.apps.EmailConfig',
41 'modules.trucking.apps.TruckingConfig',
42 'modules.customs.apps.CustomsConfig',
43 'modules.logistics.apps.LogisticsConfig',
44]
46INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
48MIDDLEWARE = [
49 'corsheaders.middleware.CorsMiddleware',
50 'django.middleware.security.SecurityMiddleware',
51 'whitenoise.middleware.WhiteNoiseMiddleware',
52 'django.contrib.sessions.middleware.SessionMiddleware',
53 'django.middleware.common.CommonMiddleware',
54 'django.middleware.csrf.CsrfViewMiddleware',
55 'django.contrib.auth.middleware.AuthenticationMiddleware',
56 'core.middleware.TenantMiddleware',
57 'django.contrib.messages.middleware.MessageMiddleware',
58 'django.middleware.clickjacking.XFrameOptionsMiddleware',
59]
61ROOT_URLCONF = 'config.urls'
63TEMPLATES = [
64 {
65 'BACKEND': 'django.template.backends.django.DjangoTemplates',
66 'DIRS': [BASE_DIR / 'templates'],
67 'APP_DIRS': True,
68 'OPTIONS': {
69 'context_processors': [
70 'django.template.context_processors.debug',
71 'django.template.context_processors.request',
72 'django.contrib.auth.context_processors.auth',
73 'django.contrib.messages.context_processors.messages',
74 ],
75 },
76 },
77]
79WSGI_APPLICATION = 'config.wsgi.application'
80ASGI_APPLICATION = 'config.asgi.application'
82# Database
83DATABASE_URL = os.getenv('DATABASE_URL')
84if DATABASE_URL:
85 try:
86 import dj_database_url
87 DATABASES = {
88 'default': dj_database_url.config(
89 default=DATABASE_URL,
90 conn_max_age=600,
91 conn_health_checks=True,
92 )
93 }
94 except Exception:
95 DATABASES = {
96 'default': {
97 'ENGINE': 'django.db.backends.sqlite3',
98 'NAME': BASE_DIR / 'db.sqlite3',
99 }
100 }
101else:
102 DATABASES = {
103 'default': {
104 'ENGINE': 'django.db.backends.sqlite3',
105 'NAME': BASE_DIR / 'db.sqlite3',
106 }
107 }
109# Custom User Model
110AUTH_USER_MODEL = 'authentication.User'
112# Password validation
113AUTH_PASSWORD_VALIDATORS = [
114 {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
115 {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': {'min_length': 8}},
116 {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
117 {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
118]
120PASSWORD_HASHERS = [
121 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
122 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
123 'django.contrib.auth.hashers.Argon2PasswordHasher',
124]
126# Internationalization
127LANGUAGE_CODE = 'en-us'
128TIME_ZONE = 'UTC'
129USE_I18N = True
130USE_TZ = True
132# Static files
133STATIC_URL = '/static/'
134STATIC_ROOT = BASE_DIR / 'staticfiles'
135MEDIA_URL = '/media/'
136MEDIA_ROOT = BASE_DIR / 'media'
138# Default primary key field type
139DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
141# Channels / Redis Layer
142REDIS_URL = os.getenv('REDIS_URL', 'redis://localhost:6379/0')
143CHANNEL_LAYER_REDIS = os.getenv('CHANNEL_LAYER_REDIS', REDIS_URL)
145try:
146 CHANNEL_LAYERS = {
147 'default': {
148 'BACKEND': 'channels_redis.core.RedisChannelLayer',
149 'CONFIG': {
150 'hosts': [CHANNEL_LAYER_REDIS],
151 },
152 },
153 }
154except Exception:
155 CHANNEL_LAYERS = {
156 'default': {
157 'BACKEND': 'channels.layers.InMemoryChannelLayer',
158 }
159 }
161# CORS configuration
162CORS_ALLOW_ALL_ORIGINS = True
163CORS_ALLOW_CREDENTIALS = True
164CORS_ALLOWED_ORIGINS = [
165 'http://localhost:5173',
166 'http://localhost:3000',
167 'http://127.0.0.1:5173',
168 'http://127.0.0.1:3000',
169]
171# JWT Settings
172JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY', SECRET_KEY)
173JWT_ALGORITHM = 'HS256'
174JWT_ACCESS_TOKEN_LIFETIME_MINUTES = int(os.getenv('JWT_ACCESS_TOKEN_LIFETIME_MINUTES', 60))
175JWT_REFRESH_TOKEN_LIFETIME_DAYS = int(os.getenv('JWT_REFRESH_TOKEN_LIFETIME_DAYS', 7))
176PASSWORD_RESET_TOKEN_LIFETIME_MINUTES = int(os.getenv('PASSWORD_RESET_TOKEN_LIFETIME_MINUTES', 30))
177PASSWORD_RESET_REQUEST_LIMIT = int(os.getenv('PASSWORD_RESET_REQUEST_LIMIT', 5))
178PASSWORD_RESET_REQUEST_WINDOW_SECONDS = int(os.getenv('PASSWORD_RESET_REQUEST_WINDOW_SECONDS', 3600))
179FRONTEND_URL = os.getenv('FRONTEND_URL', 'http://localhost:5173').rstrip('/')
181# Email configuration
182EMAIL_ENABLED = os.getenv('EMAIL_ENABLED', 'False').lower() in {'1', 'true', 'yes', 'on'}
183EMAIL_PROVIDER = os.getenv('EMAIL_PROVIDER', 'smtp').lower()
184DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'no-reply@fudo.local')
185SERVER_EMAIL = os.getenv('SERVER_EMAIL', DEFAULT_FROM_EMAIL)
187if EMAIL_PROVIDER == 'resend':
188 EMAIL_BACKEND = os.getenv('EMAIL_BACKEND', 'django.core.mail.backends.smtp.EmailBackend')
189 EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp.resend.com')
190 EMAIL_PORT = int(os.getenv('EMAIL_PORT', 587))
191 EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', 'True').lower() in {'1', 'true', 'yes', 'on'}
192 EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', 'False').lower() in {'1', 'true', 'yes', 'on'}
193 EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '')
194 EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')
195elif EMAIL_PROVIDER == 'sendgrid':
196 EMAIL_BACKEND = os.getenv('EMAIL_BACKEND', 'django.core.mail.backends.smtp.EmailBackend')
197 EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp.sendgrid.net')
198 EMAIL_PORT = int(os.getenv('EMAIL_PORT', 587))
199 EMAIL_USE_TLS = True
200 EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', 'apikey')
201 EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')
202else:
203 EMAIL_BACKEND = os.getenv('EMAIL_BACKEND', 'django.core.mail.backends.console.EmailBackend')
204 EMAIL_HOST = os.getenv('EMAIL_HOST', 'localhost')
205 EMAIL_PORT = int(os.getenv('EMAIL_PORT', 25))
206 EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', 'False').lower() in {'1', 'true', 'yes', 'on'}
207 EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', 'False').lower() in {'1', 'true', 'yes', 'on'}
208 EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '')
209 EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')