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

1import os 

2from pathlib import Path 

3import dj_database_url 

4from dotenv import load_dotenv 

5 

6load_dotenv() 

7 

8BASE_DIR = Path(__file__).resolve().parent.parent.parent 

9 

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(',')] 

13 

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] 

24 

25THIRD_PARTY_APPS = [ 

26 'corsheaders', 

27 'channels', 

28] 

29 

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] 

45 

46INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS 

47 

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] 

60 

61ROOT_URLCONF = 'config.urls' 

62 

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] 

78 

79WSGI_APPLICATION = 'config.wsgi.application' 

80ASGI_APPLICATION = 'config.asgi.application' 

81 

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 } 

108 

109# Custom User Model 

110AUTH_USER_MODEL = 'authentication.User' 

111 

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] 

119 

120PASSWORD_HASHERS = [ 

121 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 

122 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 

123 'django.contrib.auth.hashers.Argon2PasswordHasher', 

124] 

125 

126# Internationalization 

127LANGUAGE_CODE = 'en-us' 

128TIME_ZONE = 'UTC' 

129USE_I18N = True 

130USE_TZ = True 

131 

132# Static files 

133STATIC_URL = '/static/' 

134STATIC_ROOT = BASE_DIR / 'staticfiles' 

135MEDIA_URL = '/media/' 

136MEDIA_ROOT = BASE_DIR / 'media' 

137 

138# Default primary key field type 

139DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 

140 

141# Channels / Redis Layer 

142REDIS_URL = os.getenv('REDIS_URL', 'redis://localhost:6379/0') 

143CHANNEL_LAYER_REDIS = os.getenv('CHANNEL_LAYER_REDIS', REDIS_URL) 

144 

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 } 

160 

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] 

170 

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('/') 

180 

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) 

186 

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', '')