django-cfg 1.2.31__py3-none-any.whl → 1.3.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/api/health/views.py +4 -2
- django_cfg/apps/knowbase/config/settings.py +16 -15
- django_cfg/apps/payments/README.md +326 -0
- django_cfg/apps/payments/admin/__init__.py +20 -10
- django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
- django_cfg/apps/payments/admin/balance_admin.py +592 -297
- django_cfg/apps/payments/admin/currencies_admin.py +526 -222
- django_cfg/apps/payments/admin/filters.py +306 -199
- django_cfg/apps/payments/admin/payments_admin.py +465 -70
- django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
- django_cfg/apps/payments/admin_interface/__init__.py +18 -0
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
- django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
- django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
- django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
- django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
- django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
- django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
- django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
- django_cfg/apps/payments/apps.py +34 -9
- django_cfg/apps/payments/config/__init__.py +28 -51
- django_cfg/apps/payments/config/constance/__init__.py +22 -0
- django_cfg/apps/payments/config/constance/config_service.py +123 -0
- django_cfg/apps/payments/config/constance/fields.py +69 -0
- django_cfg/apps/payments/config/constance/settings.py +160 -0
- django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
- django_cfg/apps/payments/config/helpers.py +130 -0
- django_cfg/apps/payments/management/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/__init__.py +1 -3
- django_cfg/apps/payments/management/commands/cleanup_expired_data.py +419 -0
- django_cfg/apps/payments/management/commands/currency_stats.py +297 -225
- django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
- django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
- django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
- django_cfg/apps/payments/management/commands/test_providers.py +434 -0
- django_cfg/apps/payments/middleware/__init__.py +3 -1
- django_cfg/apps/payments/middleware/api_access.py +329 -222
- django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
- django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
- django_cfg/apps/payments/migrations/0001_initial.py +708 -536
- django_cfg/apps/payments/models/__init__.py +13 -18
- django_cfg/apps/payments/models/api_keys.py +121 -43
- django_cfg/apps/payments/models/balance.py +153 -115
- django_cfg/apps/payments/models/base.py +68 -15
- django_cfg/apps/payments/models/currencies.py +172 -148
- django_cfg/apps/payments/models/managers/__init__.py +44 -0
- django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
- django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
- django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
- django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
- django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
- django_cfg/apps/payments/models/payments.py +235 -285
- django_cfg/apps/payments/models/subscriptions.py +257 -177
- django_cfg/apps/payments/models/tariffs.py +147 -40
- django_cfg/apps/payments/services/__init__.py +209 -56
- django_cfg/apps/payments/services/cache/__init__.py +6 -6
- django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
- django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
- django_cfg/apps/payments/services/{cache/base.py → cache_service/interfaces.py} +3 -1
- django_cfg/apps/payments/services/cache_service/keys.py +49 -0
- django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
- django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
- django_cfg/apps/payments/services/core/__init__.py +10 -6
- django_cfg/apps/payments/services/core/balance_service.py +435 -360
- django_cfg/apps/payments/services/core/base.py +166 -0
- django_cfg/apps/payments/services/core/currency_service.py +478 -0
- django_cfg/apps/payments/services/core/payment_service.py +371 -465
- django_cfg/apps/payments/services/core/subscription_service.py +425 -481
- django_cfg/apps/payments/services/core/webhook_service.py +410 -0
- django_cfg/apps/payments/services/integrations/__init__.py +29 -0
- django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
- django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
- django_cfg/apps/payments/services/providers/__init__.py +9 -14
- django_cfg/apps/payments/services/providers/base.py +234 -174
- django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
- django_cfg/apps/payments/services/providers/registry.py +367 -301
- django_cfg/apps/payments/services/types/__init__.py +78 -0
- django_cfg/apps/payments/services/types/data.py +177 -0
- django_cfg/apps/payments/services/types/requests.py +150 -0
- django_cfg/apps/payments/services/types/responses.py +156 -0
- django_cfg/apps/payments/services/types/webhooks.py +232 -0
- django_cfg/apps/payments/signals/__init__.py +33 -8
- django_cfg/apps/payments/signals/api_key_signals.py +210 -129
- django_cfg/apps/payments/signals/balance_signals.py +174 -0
- django_cfg/apps/payments/signals/payment_signals.py +128 -103
- django_cfg/apps/payments/signals/subscription_signals.py +194 -142
- django_cfg/apps/payments/static/payments/css/components.css +380 -0
- django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
- django_cfg/apps/payments/static/payments/js/components.js +545 -0
- django_cfg/apps/payments/static/payments/js/utils.js +412 -0
- django_cfg/apps/payments/templatetags/__init__.py +1 -1
- django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
- django_cfg/apps/payments/urls.py +45 -48
- django_cfg/apps/payments/urls_admin.py +33 -42
- django_cfg/apps/payments/views/api/__init__.py +101 -0
- django_cfg/apps/payments/views/api/api_keys.py +387 -0
- django_cfg/apps/payments/views/api/balances.py +381 -0
- django_cfg/apps/payments/views/api/base.py +298 -0
- django_cfg/apps/payments/views/api/currencies.py +402 -0
- django_cfg/apps/payments/views/api/payments.py +415 -0
- django_cfg/apps/payments/views/api/subscriptions.py +475 -0
- django_cfg/apps/payments/views/api/webhooks.py +476 -0
- django_cfg/apps/payments/views/serializers/__init__.py +99 -0
- django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
- django_cfg/apps/payments/views/serializers/balances.py +300 -0
- django_cfg/apps/payments/views/serializers/currencies.py +335 -0
- django_cfg/apps/payments/views/serializers/payments.py +387 -0
- django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
- django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
- django_cfg/config.py +1 -1
- django_cfg/core/config.py +40 -4
- django_cfg/core/generation.py +25 -4
- django_cfg/core/integration/README.md +363 -0
- django_cfg/core/integration/__init__.py +47 -0
- django_cfg/core/integration/commands_collector.py +239 -0
- django_cfg/core/integration/display/__init__.py +15 -0
- django_cfg/core/integration/display/base.py +157 -0
- django_cfg/core/integration/display/ngrok.py +164 -0
- django_cfg/core/integration/display/startup.py +815 -0
- django_cfg/core/integration/url_integration.py +123 -0
- django_cfg/core/integration/version_checker.py +160 -0
- django_cfg/management/commands/auto_generate.py +4 -0
- django_cfg/management/commands/check_settings.py +6 -0
- django_cfg/management/commands/clear_constance.py +5 -2
- django_cfg/management/commands/create_token.py +6 -0
- django_cfg/management/commands/list_urls.py +6 -0
- django_cfg/management/commands/migrate_all.py +6 -0
- django_cfg/management/commands/migrator.py +3 -0
- django_cfg/management/commands/rundramatiq.py +6 -0
- django_cfg/management/commands/runserver_ngrok.py +51 -29
- django_cfg/management/commands/script.py +6 -0
- django_cfg/management/commands/show_config.py +12 -2
- django_cfg/management/commands/show_urls.py +4 -0
- django_cfg/management/commands/superuser.py +6 -0
- django_cfg/management/commands/task_clear.py +4 -1
- django_cfg/management/commands/task_status.py +3 -1
- django_cfg/management/commands/test_email.py +3 -0
- django_cfg/management/commands/test_telegram.py +6 -0
- django_cfg/management/commands/test_twilio.py +6 -0
- django_cfg/management/commands/tree.py +6 -0
- django_cfg/management/commands/validate_config.py +155 -149
- django_cfg/models/constance.py +31 -11
- django_cfg/models/payments.py +175 -492
- django_cfg/modules/django_logger.py +160 -146
- django_cfg/modules/django_unfold/dashboard.py +64 -16
- django_cfg/registry/core.py +1 -0
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/utils/smart_defaults.py +227 -570
- django_cfg/utils/toolkit.py +51 -11
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/METADATA +4 -1
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/RECORD +162 -185
- django_cfg/apps/payments/__init__.py +0 -8
- django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
- django_cfg/apps/payments/config/module.py +0 -70
- django_cfg/apps/payments/config/providers.py +0 -105
- django_cfg/apps/payments/config/settings.py +0 -96
- django_cfg/apps/payments/config/utils.py +0 -52
- django_cfg/apps/payments/decorators.py +0 -291
- django_cfg/apps/payments/management/commands/README.md +0 -146
- django_cfg/apps/payments/managers/__init__.py +0 -23
- django_cfg/apps/payments/managers/api_key_manager.py +0 -35
- django_cfg/apps/payments/managers/balance_manager.py +0 -361
- django_cfg/apps/payments/managers/currency_manager.py +0 -306
- django_cfg/apps/payments/managers/payment_manager.py +0 -192
- django_cfg/apps/payments/managers/subscription_manager.py +0 -37
- django_cfg/apps/payments/managers/tariff_manager.py +0 -29
- django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
- django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
- django_cfg/apps/payments/models/events.py +0 -73
- django_cfg/apps/payments/serializers/__init__.py +0 -57
- django_cfg/apps/payments/serializers/api_keys.py +0 -51
- django_cfg/apps/payments/serializers/balance.py +0 -59
- django_cfg/apps/payments/serializers/currencies.py +0 -63
- django_cfg/apps/payments/serializers/payments.py +0 -62
- django_cfg/apps/payments/serializers/subscriptions.py +0 -71
- django_cfg/apps/payments/serializers/tariffs.py +0 -56
- django_cfg/apps/payments/services/billing/__init__.py +0 -8
- django_cfg/apps/payments/services/cache/simple_cache.py +0 -135
- django_cfg/apps/payments/services/core/fallback_service.py +0 -432
- django_cfg/apps/payments/services/internal_types.py +0 -461
- django_cfg/apps/payments/services/middleware/__init__.py +0 -8
- django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
- django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
- django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
- django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
- django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
- django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
- django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
- django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
- django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
- django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
- django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
- django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
- django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
- django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
- django_cfg/apps/payments/services/security/__init__.py +0 -34
- django_cfg/apps/payments/services/security/error_handler.py +0 -635
- django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
- django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
- django_cfg/apps/payments/static/payments/css/payments.css +0 -340
- django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
- django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
- django_cfg/apps/payments/static/payments/js/theme.js +0 -86
- django_cfg/apps/payments/tasks/__init__.py +0 -12
- django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
- django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
- django_cfg/apps/payments/templates/payments/base.html +0 -182
- django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
- django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
- django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
- django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
- django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
- django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
- django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
- django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
- django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
- django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
- django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
- django_cfg/apps/payments/templates/payments/stats.html +0 -261
- django_cfg/apps/payments/templates/payments/test.html +0 -213
- django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
- django_cfg/apps/payments/utils/__init__.py +0 -43
- django_cfg/apps/payments/utils/billing_utils.py +0 -342
- django_cfg/apps/payments/utils/config_utils.py +0 -239
- django_cfg/apps/payments/utils/middleware_utils.py +0 -228
- django_cfg/apps/payments/utils/validation_utils.py +0 -94
- django_cfg/apps/payments/views/__init__.py +0 -63
- django_cfg/apps/payments/views/api_key_views.py +0 -164
- django_cfg/apps/payments/views/balance_views.py +0 -75
- django_cfg/apps/payments/views/currency_views.py +0 -122
- django_cfg/apps/payments/views/payment_views.py +0 -149
- django_cfg/apps/payments/views/subscription_views.py +0 -135
- django_cfg/apps/payments/views/tariff_views.py +0 -131
- django_cfg/apps/payments/views/templates/__init__.py +0 -25
- django_cfg/apps/payments/views/templates/ajax.py +0 -451
- django_cfg/apps/payments/views/templates/base.py +0 -212
- django_cfg/apps/payments/views/templates/dashboard.py +0 -60
- django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
- django_cfg/apps/payments/views/templates/payment_management.py +0 -158
- django_cfg/apps/payments/views/templates/qr_code.py +0 -174
- django_cfg/apps/payments/views/templates/stats.py +0 -244
- django_cfg/apps/payments/views/templates/utils.py +0 -181
- django_cfg/apps/payments/views/webhook_views.py +0 -266
- django_cfg/apps/payments/viewsets.py +0 -66
- django_cfg/core/integration.py +0 -160
- django_cfg/template_archive/.gitignore +0 -1
- django_cfg/template_archive/__init__.py +0 -0
- django_cfg/urls.py +0 -33
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/WHEEL +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,11 +1,10 @@
|
|
1
1
|
"""
|
2
2
|
Smart defaults system for django_cfg.
|
3
3
|
|
4
|
-
Following
|
5
|
-
-
|
6
|
-
-
|
7
|
-
-
|
8
|
-
- No mutable default arguments
|
4
|
+
Following KISS principle:
|
5
|
+
- Simple, clear configuration
|
6
|
+
- No complex environment logic
|
7
|
+
- Logging handled by django_logger module
|
9
8
|
"""
|
10
9
|
|
11
10
|
from typing import Dict, Any, Optional, List
|
@@ -16,486 +15,91 @@ from django_cfg.models.services import EmailConfig
|
|
16
15
|
from django_cfg.core.exceptions import ConfigurationError
|
17
16
|
|
18
17
|
|
19
|
-
|
18
|
+
def get_log_filename() -> str:
|
20
19
|
"""
|
21
|
-
|
20
|
+
Determine the correct log filename based on project type.
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
- Available services (Redis, SMTP, etc.)
|
27
|
-
- Security requirements
|
22
|
+
Returns:
|
23
|
+
- 'django-cfg.log' for django-cfg projects
|
24
|
+
- 'django.log' for regular Django projects
|
28
25
|
"""
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
) -> CacheConfig:
|
37
|
-
"""
|
38
|
-
Configure cache backend with environment-aware defaults.
|
26
|
+
try:
|
27
|
+
# Check for django-cfg in installed apps
|
28
|
+
from django.conf import settings
|
29
|
+
if hasattr(settings, 'INSTALLED_APPS'):
|
30
|
+
for app in settings.INSTALLED_APPS:
|
31
|
+
if 'django_cfg' in app:
|
32
|
+
return 'django-cfg.log'
|
39
33
|
|
40
|
-
|
41
|
-
|
42
|
-
environment: Current environment
|
43
|
-
debug: Django DEBUG setting
|
44
|
-
|
45
|
-
Returns:
|
46
|
-
Configured cache backend
|
47
|
-
|
48
|
-
Raises:
|
49
|
-
ConfigurationError: If configuration cannot be applied
|
50
|
-
"""
|
51
|
-
try:
|
52
|
-
# Create a copy to avoid modifying the original
|
53
|
-
config: CacheConfig = cache_config.model_copy()
|
54
|
-
|
55
|
-
# Environment-specific adjustments
|
56
|
-
if environment == "testing":
|
57
|
-
# Testing: Use dummy cache with very short timeouts
|
58
|
-
config.backend_override = "django.core.cache.backends.dummy.DummyCache"
|
59
|
-
config.timeout = min(config.timeout, 1) # Max 1 second for tests
|
60
|
-
|
61
|
-
elif environment == "development" or debug:
|
62
|
-
# Development: Prefer memory cache, shorter timeouts
|
63
|
-
if not config.redis_url:
|
64
|
-
# No Redis configured - use memory cache
|
65
|
-
config.timeout = min(config.timeout, 300) # Max 5 minutes
|
66
|
-
else:
|
67
|
-
# Redis available - use it but with shorter timeouts
|
68
|
-
config.timeout = min(config.timeout, 600) # Max 10 minutes
|
69
|
-
config.max_connections = min(config.max_connections, 20) # Fewer connections
|
70
|
-
|
71
|
-
elif environment in ("production", "staging"):
|
72
|
-
# Production: Use Redis if available, longer timeouts
|
73
|
-
if config.redis_url:
|
74
|
-
config.timeout = max(config.timeout, 300) # Min 5 minutes
|
75
|
-
config.max_connections = max(config.max_connections, 50) # More connections
|
76
|
-
else:
|
77
|
-
# Production without Redis - warn but use file cache
|
78
|
-
config.backend_override = "django.core.cache.backends.filebased.FileBasedCache"
|
79
|
-
|
80
|
-
# Apply compression for production
|
81
|
-
if environment == "production" and config.redis_url:
|
82
|
-
config.compress = True
|
83
|
-
|
84
|
-
return config
|
85
|
-
|
86
|
-
except Exception as e:
|
87
|
-
raise ConfigurationError(
|
88
|
-
f"Failed to configure cache backend: {e}",
|
89
|
-
context={
|
90
|
-
'environment': environment,
|
91
|
-
'debug': debug,
|
92
|
-
'cache_config': cache_config.model_dump()
|
93
|
-
}
|
94
|
-
) from e
|
95
|
-
|
96
|
-
@classmethod
|
97
|
-
def configure_email_backend(
|
98
|
-
cls,
|
99
|
-
email_config: EmailConfig,
|
100
|
-
environment: Optional[str] = None,
|
101
|
-
debug: bool = False
|
102
|
-
) -> EmailConfig:
|
103
|
-
"""
|
104
|
-
Configure email backend with environment-aware defaults.
|
34
|
+
# Default to regular Django log
|
35
|
+
return 'django.log'
|
105
36
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
Raises:
|
115
|
-
ConfigurationError: If configuration cannot be applied
|
116
|
-
"""
|
117
|
-
try:
|
118
|
-
# Create a copy to avoid modifying the original
|
119
|
-
config = email_config.model_copy()
|
120
|
-
|
121
|
-
# Environment-specific adjustments
|
122
|
-
if environment == "testing":
|
123
|
-
# Testing: Use in-memory backend
|
124
|
-
config.backend_override = "django.core.mail.backends.locmem.EmailBackend"
|
125
|
-
config.timeout = min(config.timeout, 5) # Very short timeout
|
126
|
-
|
127
|
-
elif environment == "development":
|
128
|
-
# Development: Use SMTP if configured (allow real email sending in dev)
|
129
|
-
if config.username and config.password:
|
130
|
-
# SMTP configured - use SMTP backend
|
131
|
-
config.backend_override = "django.core.mail.backends.smtp.EmailBackend"
|
132
|
-
# Note: No fallback to console - let user decide via backend setting
|
133
|
-
|
134
|
-
elif environment in ("production", "staging"):
|
135
|
-
# Production: Use SMTP if properly configured
|
136
|
-
if config.username and config.password:
|
137
|
-
config.backend_override = "django.core.mail.backends.smtp.EmailBackend"
|
138
|
-
|
139
|
-
# Production email security
|
140
|
-
if config.port == 587:
|
141
|
-
config.use_tls = True
|
142
|
-
config.use_ssl = False
|
143
|
-
elif config.port == 465:
|
144
|
-
config.use_tls = False
|
145
|
-
config.use_ssl = True
|
146
|
-
|
147
|
-
# Longer timeout for production
|
148
|
-
config.timeout = max(config.timeout, 30)
|
149
|
-
else:
|
150
|
-
# Production without SMTP - fallback to console with warning
|
151
|
-
config.backend_override = "django.core.mail.backends.console.EmailBackend"
|
152
|
-
|
153
|
-
return config
|
154
|
-
|
155
|
-
except Exception as e:
|
156
|
-
raise ConfigurationError(
|
157
|
-
f"Failed to configure email backend: {e}",
|
158
|
-
context={
|
159
|
-
'environment': environment,
|
160
|
-
'debug': debug,
|
161
|
-
'email_config': email_config.model_dump(exclude={'password'})
|
162
|
-
}
|
163
|
-
) from e
|
37
|
+
except Exception:
|
38
|
+
# Fallback to django-cfg filename (since we're in django-cfg module)
|
39
|
+
return 'django-cfg.log'
|
40
|
+
|
41
|
+
|
42
|
+
class SmartDefaults:
|
43
|
+
"""
|
44
|
+
Environment-aware smart defaults for Django configuration.
|
164
45
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
domains: List[str],
|
169
|
-
environment: Optional[str] = None,
|
170
|
-
debug: bool = False,
|
171
|
-
ssl_redirect: Optional[bool] = None,
|
172
|
-
cors_allow_headers: Optional[List[str]] = None
|
173
|
-
) -> Dict[str, Any]:
|
174
|
-
"""
|
175
|
-
Get security defaults based on environment and domains.
|
176
|
-
|
177
|
-
Args:
|
178
|
-
domains: List of domains for CORS/security configuration
|
179
|
-
environment: Current environment
|
180
|
-
debug: Django DEBUG setting
|
181
|
-
ssl_redirect: Force SSL redirect on/off (None = auto based on domains)
|
182
|
-
cors_allow_headers: Additional CORS headers to extend defaults
|
183
|
-
|
184
|
-
Returns:
|
185
|
-
Security settings dictionary
|
186
|
-
"""
|
187
|
-
try:
|
188
|
-
settings = {}
|
189
|
-
|
190
|
-
if not domains:
|
191
|
-
return settings
|
192
|
-
|
193
|
-
is_dev = environment == "development" or debug
|
194
|
-
|
195
|
-
# Generate CORS settings
|
196
|
-
cors_settings = cls._generate_cors_settings(domains, is_dev, cors_allow_headers)
|
197
|
-
settings.update(cors_settings)
|
198
|
-
|
199
|
-
# Generate CSRF trusted origins
|
200
|
-
csrf_settings = cls._generate_csrf_settings(domains, is_dev)
|
201
|
-
settings.update(csrf_settings)
|
202
|
-
|
203
|
-
# Generate security headers for production
|
204
|
-
if environment == "production":
|
205
|
-
security_headers = cls._generate_security_headers(domains, ssl_redirect)
|
206
|
-
settings.update(security_headers)
|
207
|
-
|
208
|
-
return settings
|
209
|
-
|
210
|
-
except Exception as e:
|
211
|
-
raise ConfigurationError(
|
212
|
-
f"Failed to generate security defaults: {e}",
|
213
|
-
context={
|
214
|
-
'domains': domains,
|
215
|
-
'environment': environment,
|
216
|
-
'debug': debug
|
217
|
-
}
|
218
|
-
) from e
|
46
|
+
Provides intelligent defaults based on environment detection
|
47
|
+
with comprehensive type safety and validation.
|
48
|
+
"""
|
219
49
|
|
220
|
-
@
|
221
|
-
def
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
'CORS_ALLOW_CREDENTIALS': True,
|
230
|
-
'CORS_ALLOW_HEADERS': cls._get_cors_headers(cors_allow_headers)
|
50
|
+
@staticmethod
|
51
|
+
def get_database_defaults(environment: str = "development", debug: bool = False, engine: str = "sqlite3") -> Dict[str, Any]:
|
52
|
+
"""Get database configuration defaults."""
|
53
|
+
defaults = {
|
54
|
+
'ENGINE': 'django.db.backends.sqlite3',
|
55
|
+
'NAME': Path('db') / 'db.sqlite3',
|
56
|
+
'ATOMIC_REQUESTS': True,
|
57
|
+
'CONN_MAX_AGE': 60,
|
58
|
+
'OPTIONS': {}
|
231
59
|
}
|
232
60
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
settings['CORS_ALLOWED_ORIGINS'] = cls._build_allowed_origins(domains)
|
239
|
-
|
240
|
-
return settings
|
241
|
-
|
242
|
-
@classmethod
|
243
|
-
def _generate_csrf_settings(cls, domains: List[str], is_dev: bool) -> Dict[str, Any]:
|
244
|
-
"""Generate CSRF trusted origins."""
|
245
|
-
if is_dev:
|
246
|
-
csrf_origins = cls._build_dev_csrf_origins(domains)
|
247
|
-
else:
|
248
|
-
csrf_origins = cls._build_prod_csrf_origins(domains)
|
249
|
-
|
250
|
-
# Only return CSRF settings if we have origins to set
|
251
|
-
if csrf_origins:
|
252
|
-
return {'CSRF_TRUSTED_ORIGINS': csrf_origins}
|
61
|
+
# Add engine-specific options
|
62
|
+
if engine == "django.db.backends.postgresql":
|
63
|
+
defaults['OPTIONS']['connect_timeout'] = 20
|
64
|
+
elif engine == "django.db.backends.sqlite3":
|
65
|
+
defaults['OPTIONS']['timeout'] = 20 # SQLite uses 'timeout'
|
253
66
|
|
254
|
-
return
|
67
|
+
return defaults
|
255
68
|
|
256
|
-
@
|
257
|
-
def
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
'SECURE_HSTS_PRELOAD': True,
|
269
|
-
'X_FRAME_OPTIONS': 'DENY',
|
69
|
+
@staticmethod
|
70
|
+
def get_cache_defaults() -> Dict[str, Any]:
|
71
|
+
"""Get cache configuration defaults."""
|
72
|
+
return {
|
73
|
+
'default': {
|
74
|
+
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
75
|
+
'LOCATION': 'default-cache',
|
76
|
+
'TIMEOUT': 300,
|
77
|
+
'OPTIONS': {
|
78
|
+
'MAX_ENTRIES': 1000,
|
79
|
+
}
|
80
|
+
}
|
270
81
|
}
|
271
|
-
|
272
|
-
# SSL settings - configurable or auto-detect based on domains
|
273
|
-
should_use_ssl = ssl_redirect if ssl_redirect is not None else bool(domains)
|
274
|
-
|
275
|
-
if should_use_ssl:
|
276
|
-
settings.update({
|
277
|
-
'SECURE_SSL_REDIRECT': True,
|
278
|
-
'SESSION_COOKIE_SECURE': True,
|
279
|
-
'CSRF_COOKIE_SECURE': True,
|
280
|
-
})
|
281
|
-
elif ssl_redirect is False:
|
282
|
-
# Explicitly disable SSL redirect
|
283
|
-
settings.update({
|
284
|
-
'SECURE_SSL_REDIRECT': False,
|
285
|
-
'SESSION_COOKIE_SECURE': False,
|
286
|
-
'CSRF_COOKIE_SECURE': False,
|
287
|
-
})
|
288
|
-
|
289
|
-
return settings
|
290
|
-
|
291
|
-
@classmethod
|
292
|
-
def _get_cors_headers(cls, cors_allow_headers: Optional[List[str]] = None) -> List[str]:
|
293
|
-
"""
|
294
|
-
Get CORS headers with defaults extended by custom headers.
|
295
|
-
|
296
|
-
Note: The default headers are defined in DjangoConfig.cors_allow_headers.
|
297
|
-
This method should be consistent with those defaults.
|
298
|
-
"""
|
299
|
-
# Default headers - should match DjangoConfig.cors_allow_headers defaults
|
300
|
-
default_headers = [
|
301
|
-
"accept",
|
302
|
-
"accept-encoding",
|
303
|
-
"authorization",
|
304
|
-
"content-type",
|
305
|
-
"dnt",
|
306
|
-
"origin",
|
307
|
-
"user-agent",
|
308
|
-
"x-csrftoken",
|
309
|
-
"x-requested-with",
|
310
|
-
"x-api-key",
|
311
|
-
"x-api-token",
|
312
|
-
]
|
313
|
-
|
314
|
-
if not cors_allow_headers:
|
315
|
-
return default_headers
|
316
|
-
|
317
|
-
# Extend with custom headers and remove duplicates while preserving order
|
318
|
-
return cls._merge_headers(default_headers, cors_allow_headers)
|
319
|
-
|
320
|
-
@classmethod
|
321
|
-
def _merge_headers(cls, default_headers: List[str], custom_headers: List[str]) -> List[str]:
|
322
|
-
"""Merge header lists removing duplicates while preserving order."""
|
323
|
-
all_headers = default_headers + custom_headers
|
324
|
-
seen = set()
|
325
|
-
unique_headers = []
|
326
|
-
|
327
|
-
for header in all_headers:
|
328
|
-
header_lower = header.lower()
|
329
|
-
if header_lower not in seen:
|
330
|
-
seen.add(header_lower)
|
331
|
-
unique_headers.append(header)
|
332
|
-
|
333
|
-
return unique_headers
|
334
|
-
|
335
|
-
@classmethod
|
336
|
-
def _build_allowed_origins(cls, domains: List[str]) -> List[str]:
|
337
|
-
"""Build CORS allowed origins for production."""
|
338
|
-
allowed_origins = []
|
339
|
-
|
340
|
-
for domain in domains:
|
341
|
-
# Add HTTPS version
|
342
|
-
allowed_origins.append(f"https://{domain}")
|
343
|
-
|
344
|
-
# Add www. version if applicable
|
345
|
-
if cls._should_add_www_variant(domain):
|
346
|
-
allowed_origins.append(f"https://www.{domain}")
|
347
|
-
|
348
|
-
return allowed_origins
|
349
82
|
|
350
|
-
@
|
351
|
-
def
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
for domain in domains:
|
356
|
-
if domain.startswith(('localhost', '127.0.0.1', '0.0.0.0')):
|
357
|
-
# Local domains: add HTTP with common ports
|
358
|
-
csrf_origins.extend([
|
359
|
-
f"http://{domain}",
|
360
|
-
f"http://{domain}:8000",
|
361
|
-
f"http://{domain}:3000",
|
362
|
-
])
|
363
|
-
elif domain.endswith('.local'):
|
364
|
-
# .local domains: add both HTTP and HTTPS
|
365
|
-
csrf_origins.extend([
|
366
|
-
f"http://{domain}",
|
367
|
-
f"https://{domain}",
|
368
|
-
])
|
369
|
-
else:
|
370
|
-
# External domains: add both for dev flexibility
|
371
|
-
csrf_origins.extend([
|
372
|
-
f"https://{domain}",
|
373
|
-
f"http://{domain}",
|
374
|
-
])
|
375
|
-
|
376
|
-
return csrf_origins
|
377
|
-
|
378
|
-
@classmethod
|
379
|
-
def _build_prod_csrf_origins(cls, domains: List[str]) -> List[str]:
|
380
|
-
"""Build CSRF trusted origins for production."""
|
381
|
-
csrf_origins = []
|
382
|
-
|
383
|
-
for domain in domains:
|
384
|
-
# Add HTTPS version
|
385
|
-
csrf_origins.append(f"https://{domain}")
|
386
|
-
|
387
|
-
# Add www. version if applicable
|
388
|
-
if cls._should_add_www_variant(domain):
|
389
|
-
csrf_origins.append(f"https://www.{domain}")
|
390
|
-
|
391
|
-
return csrf_origins
|
392
|
-
|
393
|
-
@classmethod
|
394
|
-
def _should_add_www_variant(cls, domain: str) -> bool:
|
395
|
-
"""
|
396
|
-
Check if domain should get a www. variant added.
|
397
|
-
|
398
|
-
Simple rule: add www. variant only if domain doesn't already start with www.
|
399
|
-
Let users handle complex subdomain logic themselves by providing exact domains they want.
|
400
|
-
|
401
|
-
Examples:
|
402
|
-
- example.com -> True (add www.example.com)
|
403
|
-
- www.example.com -> False (already has www)
|
404
|
-
- api.example.com -> True (add www.api.example.com - let user decide)
|
405
|
-
- localhost -> False (no dot)
|
406
|
-
"""
|
407
|
-
if not domain or '.' not in domain:
|
408
|
-
return False
|
409
|
-
|
410
|
-
# Don't add www if domain already starts with www
|
411
|
-
return not domain.startswith('www.')
|
412
|
-
|
413
|
-
@classmethod
|
414
|
-
def get_database_defaults(
|
415
|
-
cls,
|
416
|
-
environment: Optional[str] = None,
|
83
|
+
@staticmethod
|
84
|
+
def get_security_defaults(
|
85
|
+
security_domains=None,
|
86
|
+
environment: str = "development",
|
417
87
|
debug: bool = False,
|
418
|
-
|
88
|
+
ssl_redirect=None,
|
89
|
+
cors_allow_headers=None
|
419
90
|
) -> Dict[str, Any]:
|
420
|
-
"""
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
defaults = {}
|
433
|
-
|
434
|
-
if environment == "testing":
|
435
|
-
# Testing: Use in-memory SQLite
|
436
|
-
defaults = {
|
437
|
-
'ENGINE': 'django.db.backends.sqlite3',
|
438
|
-
'NAME': ':memory:',
|
439
|
-
'OPTIONS': {
|
440
|
-
'timeout': 1, # Very short timeout for tests
|
441
|
-
}
|
442
|
-
}
|
443
|
-
|
444
|
-
elif environment == "development" or debug:
|
445
|
-
# Development: Only add PostgreSQL/MySQL specific options
|
446
|
-
if engine and engine in ("django.db.backends.postgresql", "django.db.backends.mysql"):
|
447
|
-
defaults = {
|
448
|
-
'OPTIONS': {
|
449
|
-
'connect_timeout': 5, # Short timeout for dev
|
450
|
-
}
|
451
|
-
}
|
452
|
-
# Add sslmode only for PostgreSQL
|
453
|
-
if engine == "django.db.backends.postgresql":
|
454
|
-
defaults['OPTIONS']['sslmode'] = 'prefer'
|
455
|
-
else:
|
456
|
-
# For SQLite, no special options needed
|
457
|
-
defaults = {}
|
458
|
-
|
459
|
-
elif environment in ("production", "staging"):
|
460
|
-
# Production: Only add PostgreSQL/MySQL specific options
|
461
|
-
if engine and engine in ("django.db.backends.postgresql", "django.db.backends.mysql"):
|
462
|
-
if engine == "django.db.backends.postgresql":
|
463
|
-
# psycopg3 supports connection pooling with proper parameters
|
464
|
-
defaults = {
|
465
|
-
'OPTIONS': {
|
466
|
-
'connect_timeout': 10,
|
467
|
-
# psycopg3 connection pool parameters
|
468
|
-
'pool': {
|
469
|
-
'min_size': 1,
|
470
|
-
'max_size': 20,
|
471
|
-
'timeout': 30.0,
|
472
|
-
}
|
473
|
-
}
|
474
|
-
}
|
475
|
-
else:
|
476
|
-
# MySQL
|
477
|
-
defaults = {
|
478
|
-
'OPTIONS': {
|
479
|
-
'connect_timeout': 10,
|
480
|
-
}
|
481
|
-
}
|
482
|
-
# Add sslmode only for PostgreSQL
|
483
|
-
if engine == "django.db.backends.postgresql":
|
484
|
-
defaults['OPTIONS']['sslmode'] = 'require' # Require SSL in production
|
485
|
-
else:
|
486
|
-
# For SQLite, no special options needed
|
487
|
-
defaults = {}
|
488
|
-
|
489
|
-
return defaults
|
490
|
-
|
491
|
-
except Exception as e:
|
492
|
-
raise ConfigurationError(
|
493
|
-
f"Failed to generate database defaults: {e}",
|
494
|
-
context={
|
495
|
-
'environment': environment,
|
496
|
-
'debug': debug
|
497
|
-
}
|
498
|
-
) from e
|
91
|
+
"""Get security configuration defaults."""
|
92
|
+
return {
|
93
|
+
'USE_TZ': True,
|
94
|
+
'USE_I18N': True,
|
95
|
+
'USE_L10N': True,
|
96
|
+
'SECURE_BROWSER_XSS_FILTER': not debug,
|
97
|
+
'SECURE_CONTENT_TYPE_NOSNIFF': not debug,
|
98
|
+
'X_FRAME_OPTIONS': 'DENY' if not debug else 'SAMEORIGIN',
|
99
|
+
'SECURE_HSTS_SECONDS': 31536000 if not debug else 0,
|
100
|
+
'SECURE_HSTS_INCLUDE_SUBDOMAINS': not debug,
|
101
|
+
'SECURE_HSTS_PRELOAD': not debug,
|
102
|
+
}
|
499
103
|
|
500
104
|
@classmethod
|
501
105
|
def get_logging_defaults(
|
@@ -504,116 +108,169 @@ class SmartDefaults:
|
|
504
108
|
debug: bool = False
|
505
109
|
) -> Dict[str, Any]:
|
506
110
|
"""
|
507
|
-
|
111
|
+
Simple logging configuration.
|
112
|
+
|
113
|
+
NOTE: Real logging setup is handled by django_cfg.modules.django_logger
|
114
|
+
This provides minimal fallback configuration only.
|
508
115
|
|
509
116
|
Args:
|
510
|
-
environment:
|
511
|
-
debug:
|
117
|
+
environment: Environment name (ignored - for compatibility)
|
118
|
+
debug: Debug mode (ignored - for compatibility)
|
512
119
|
|
513
120
|
Returns:
|
514
|
-
|
121
|
+
Minimal Django logging configuration
|
515
122
|
"""
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
'
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
'
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
}
|
561
|
-
|
562
|
-
elif environment in ("production", "staging"):
|
563
|
-
# Production: File and structured logging
|
564
|
-
return {
|
565
|
-
'version': 1,
|
566
|
-
'disable_existing_loggers': False,
|
567
|
-
'formatters': {
|
568
|
-
'json': {
|
569
|
-
'class': 'pythonjsonlogger.jsonlogger.JsonFormatter',
|
570
|
-
'format': '%(asctime)s %(name)s %(levelname)s %(message)s'
|
571
|
-
},
|
572
|
-
'standard': {
|
573
|
-
'format': '{levelname} {asctime} {name} {message}',
|
574
|
-
'style': '{',
|
575
|
-
},
|
576
|
-
},
|
577
|
-
'handlers': {
|
578
|
-
'file': {
|
579
|
-
'class': 'logging.handlers.RotatingFileHandler',
|
580
|
-
'filename': 'logs/django.log',
|
581
|
-
'maxBytes': 1024*1024*10, # 10MB
|
582
|
-
'backupCount': 5,
|
583
|
-
'formatter': 'json' if environment == "production" else 'standard',
|
584
|
-
},
|
585
|
-
'console': {
|
586
|
-
'class': 'logging.StreamHandler',
|
587
|
-
'formatter': 'standard',
|
588
|
-
},
|
589
|
-
},
|
590
|
-
'root': {
|
591
|
-
'handlers': ['file', 'console'],
|
592
|
-
'level': 'WARNING' if environment == "production" else 'INFO',
|
593
|
-
},
|
594
|
-
'loggers': {
|
595
|
-
'django': {
|
596
|
-
'handlers': ['file'],
|
597
|
-
'level': 'INFO',
|
598
|
-
'propagate': False,
|
599
|
-
},
|
600
|
-
},
|
601
|
-
}
|
123
|
+
# Minimal fallback - actual logging is configured by django_logger module
|
124
|
+
return {
|
125
|
+
'version': 1,
|
126
|
+
'disable_existing_loggers': False,
|
127
|
+
'handlers': {
|
128
|
+
'console': {
|
129
|
+
'class': 'logging.StreamHandler',
|
130
|
+
},
|
131
|
+
},
|
132
|
+
'root': {
|
133
|
+
'handlers': ['console'],
|
134
|
+
'level': 'INFO',
|
135
|
+
},
|
136
|
+
}
|
137
|
+
|
138
|
+
@staticmethod
|
139
|
+
def get_middleware_defaults() -> List[str]:
|
140
|
+
"""Get middleware configuration defaults."""
|
141
|
+
return [
|
142
|
+
'corsheaders.middleware.CorsMiddleware',
|
143
|
+
'django.middleware.security.SecurityMiddleware',
|
144
|
+
'django.contrib.sessions.middleware.SessionMiddleware',
|
145
|
+
'django.middleware.common.CommonMiddleware',
|
146
|
+
'django.middleware.csrf.CsrfViewMiddleware',
|
147
|
+
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
148
|
+
'django.contrib.messages.middleware.MessageMiddleware',
|
149
|
+
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
150
|
+
]
|
151
|
+
|
152
|
+
@staticmethod
|
153
|
+
def get_installed_apps_defaults() -> List[str]:
|
154
|
+
"""Get default installed apps."""
|
155
|
+
return [
|
156
|
+
# Unfold admin
|
157
|
+
"unfold",
|
158
|
+
"unfold.contrib.filters",
|
159
|
+
"unfold.contrib.forms",
|
160
|
+
"unfold.contrib.inlines",
|
161
|
+
"import_export",
|
162
|
+
"unfold.contrib.import_export",
|
163
|
+
"unfold.contrib.guardian",
|
164
|
+
"unfold.contrib.simple_history",
|
165
|
+
"unfold.contrib.location_field",
|
166
|
+
"unfold.contrib.constance",
|
602
167
|
|
603
|
-
#
|
604
|
-
|
168
|
+
# Django core
|
169
|
+
"django.contrib.admin",
|
170
|
+
"django.contrib.auth",
|
171
|
+
"django.contrib.contenttypes",
|
172
|
+
"django.contrib.sessions",
|
173
|
+
"django.contrib.messages",
|
174
|
+
"django.contrib.staticfiles",
|
175
|
+
"django.contrib.humanize",
|
605
176
|
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
177
|
+
# Third-party
|
178
|
+
"corsheaders",
|
179
|
+
"rest_framework",
|
180
|
+
"rest_framework.authtoken",
|
181
|
+
"rest_framework_simplejwt",
|
182
|
+
"rest_framework_simplejwt.token_blacklist",
|
183
|
+
"rest_framework_nested",
|
184
|
+
"rangefilter",
|
185
|
+
"django_filters",
|
186
|
+
"drf_spectacular",
|
187
|
+
"drf_spectacular_sidecar",
|
188
|
+
"django_json_widget",
|
189
|
+
"django_extensions",
|
190
|
+
"constance",
|
191
|
+
"constance.backends.database",
|
192
|
+
|
193
|
+
# Django CFG
|
194
|
+
"django_cfg",
|
195
|
+
]
|
196
|
+
|
197
|
+
@staticmethod
|
198
|
+
def get_templates_defaults() -> List[Dict[str, Any]]:
|
199
|
+
"""Get templates configuration defaults."""
|
200
|
+
return [
|
201
|
+
{
|
202
|
+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
203
|
+
'DIRS': ['templates'],
|
204
|
+
'APP_DIRS': True,
|
205
|
+
'OPTIONS': {
|
206
|
+
'context_processors': [
|
207
|
+
'django.template.context_processors.debug',
|
208
|
+
'django.template.context_processors.request',
|
209
|
+
'django.contrib.auth.context_processors.auth',
|
210
|
+
'django.contrib.messages.context_processors.messages',
|
211
|
+
],
|
212
|
+
},
|
213
|
+
},
|
214
|
+
]
|
215
|
+
|
216
|
+
@staticmethod
|
217
|
+
def get_static_files_defaults() -> Dict[str, Any]:
|
218
|
+
"""Get static files configuration defaults."""
|
219
|
+
return {
|
220
|
+
'STATIC_URL': '/static/',
|
221
|
+
'STATIC_ROOT': Path('staticfiles'),
|
222
|
+
'STATICFILES_DIRS': [
|
223
|
+
Path('static'),
|
224
|
+
],
|
225
|
+
'MEDIA_URL': '/media/',
|
226
|
+
'MEDIA_ROOT': Path('media'),
|
227
|
+
}
|
228
|
+
|
229
|
+
@staticmethod
|
230
|
+
def get_rest_framework_defaults() -> Dict[str, Any]:
|
231
|
+
"""Get Django REST Framework defaults."""
|
232
|
+
return {
|
233
|
+
'DEFAULT_AUTHENTICATION_CLASSES': [
|
234
|
+
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
235
|
+
'rest_framework.authentication.SessionAuthentication',
|
236
|
+
],
|
237
|
+
'DEFAULT_PERMISSION_CLASSES': [
|
238
|
+
'rest_framework.permissions.IsAuthenticated',
|
239
|
+
],
|
240
|
+
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
241
|
+
'PAGE_SIZE': 20,
|
242
|
+
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
243
|
+
}
|
244
|
+
|
245
|
+
@staticmethod
|
246
|
+
def get_cors_defaults() -> Dict[str, Any]:
|
247
|
+
"""Get CORS configuration defaults."""
|
248
|
+
return {
|
249
|
+
'CORS_ALLOWED_ORIGINS': [
|
250
|
+
"http://localhost:3000",
|
251
|
+
"http://127.0.0.1:3000",
|
252
|
+
],
|
253
|
+
'CORS_ALLOW_CREDENTIALS': True,
|
254
|
+
'CORS_ALLOW_ALL_ORIGINS': False,
|
255
|
+
}
|
256
|
+
|
257
|
+
@staticmethod
|
258
|
+
def configure_cache_backend(cache_config, environment: str, debug: bool):
|
259
|
+
"""Configure cache backend - simplified."""
|
260
|
+
if cache_config is None:
|
261
|
+
return CacheConfig()
|
262
|
+
return cache_config
|
263
|
+
|
264
|
+
@staticmethod
|
265
|
+
def configure_email_backend(email_config, environment: str, debug: bool):
|
266
|
+
"""Configure email backend - simplified."""
|
267
|
+
if email_config is None:
|
268
|
+
return EmailConfig()
|
269
|
+
return email_config
|
614
270
|
|
615
271
|
|
616
272
|
# Export the main class
|
617
273
|
__all__ = [
|
618
274
|
"SmartDefaults",
|
275
|
+
"get_log_filename",
|
619
276
|
]
|