django-cfg 1.2.29__py3-none-any.whl → 1.3.1__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.
Files changed (258) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/health/views.py +4 -2
  3. django_cfg/apps/knowbase/config/settings.py +16 -15
  4. django_cfg/apps/payments/README.md +326 -0
  5. django_cfg/apps/payments/admin/__init__.py +20 -9
  6. django_cfg/apps/payments/admin/api_keys_admin.py +521 -237
  7. django_cfg/apps/payments/admin/balance_admin.py +592 -297
  8. django_cfg/apps/payments/admin/currencies_admin.py +600 -108
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +470 -64
  11. django_cfg/apps/payments/admin/subscriptions_admin.py +578 -128
  12. django_cfg/apps/payments/admin_interface/__init__.py +18 -0
  13. django_cfg/apps/payments/admin_interface/templates/payments/base.html +162 -0
  14. django_cfg/apps/payments/admin_interface/templates/payments/components/dev_tool_card.html +38 -0
  15. django_cfg/apps/payments/admin_interface/templates/payments/components/loading_spinner.html +16 -0
  16. django_cfg/apps/payments/admin_interface/templates/payments/components/notification.html +27 -0
  17. django_cfg/apps/payments/admin_interface/templates/payments/components/provider_card.html +86 -0
  18. django_cfg/apps/payments/admin_interface/templates/payments/components/status_card.html +39 -0
  19. django_cfg/apps/payments/admin_interface/templates/payments/currency_converter.html +382 -0
  20. django_cfg/apps/payments/admin_interface/templates/payments/payment_dashboard.html +300 -0
  21. django_cfg/apps/payments/admin_interface/templates/payments/payment_form.html +303 -0
  22. django_cfg/apps/payments/admin_interface/templates/payments/payment_list.html +382 -0
  23. django_cfg/apps/payments/admin_interface/templates/payments/payment_status.html +500 -0
  24. django_cfg/apps/payments/admin_interface/templates/payments/webhook_dashboard.html +594 -0
  25. django_cfg/apps/payments/admin_interface/views/__init__.py +23 -0
  26. django_cfg/apps/payments/admin_interface/views/payment_views.py +259 -0
  27. django_cfg/apps/payments/admin_interface/views/webhook_dashboard.py +37 -0
  28. django_cfg/apps/payments/apps.py +34 -9
  29. django_cfg/apps/payments/config/__init__.py +28 -51
  30. django_cfg/apps/payments/config/constance/__init__.py +22 -0
  31. django_cfg/apps/payments/config/constance/config_service.py +123 -0
  32. django_cfg/apps/payments/config/constance/fields.py +69 -0
  33. django_cfg/apps/payments/config/constance/settings.py +160 -0
  34. django_cfg/apps/payments/config/django_cfg_integration.py +202 -0
  35. django_cfg/apps/payments/config/helpers.py +130 -0
  36. django_cfg/apps/payments/management/__init__.py +1 -3
  37. django_cfg/apps/payments/management/commands/__init__.py +1 -3
  38. django_cfg/apps/payments/management/commands/manage_currencies.py +381 -0
  39. django_cfg/apps/payments/management/commands/manage_providers.py +408 -0
  40. django_cfg/apps/payments/middleware/__init__.py +3 -1
  41. django_cfg/apps/payments/middleware/api_access.py +329 -222
  42. django_cfg/apps/payments/middleware/rate_limiting.py +343 -163
  43. django_cfg/apps/payments/middleware/usage_tracking.py +250 -238
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +16 -20
  46. django_cfg/apps/payments/models/api_keys.py +121 -43
  47. django_cfg/apps/payments/models/balance.py +150 -115
  48. django_cfg/apps/payments/models/base.py +68 -15
  49. django_cfg/apps/payments/models/currencies.py +207 -67
  50. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  51. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  52. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  53. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  54. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  55. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  56. django_cfg/apps/payments/models/payments.py +235 -284
  57. django_cfg/apps/payments/models/subscriptions.py +257 -177
  58. django_cfg/apps/payments/models/tariffs.py +147 -40
  59. django_cfg/apps/payments/services/__init__.py +209 -56
  60. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  61. django_cfg/apps/payments/services/cache/{simple_cache.py → cache_service.py} +112 -12
  62. django_cfg/apps/payments/services/core/__init__.py +10 -6
  63. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  64. django_cfg/apps/payments/services/core/base.py +166 -0
  65. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  66. django_cfg/apps/payments/services/core/payment_service.py +344 -468
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -484
  68. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  69. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  70. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  71. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  72. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  73. django_cfg/apps/payments/services/providers/base.py +232 -71
  74. django_cfg/apps/payments/services/providers/nowpayments.py +404 -219
  75. django_cfg/apps/payments/services/providers/registry.py +429 -80
  76. django_cfg/apps/payments/services/types/__init__.py +78 -0
  77. django_cfg/apps/payments/services/types/data.py +177 -0
  78. django_cfg/apps/payments/services/types/requests.py +150 -0
  79. django_cfg/apps/payments/services/types/responses.py +156 -0
  80. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  81. django_cfg/apps/payments/signals/__init__.py +33 -8
  82. django_cfg/apps/payments/signals/api_key_signals.py +211 -130
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +129 -98
  85. django_cfg/apps/payments/signals/subscription_signals.py +195 -143
  86. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  87. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  88. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  89. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  90. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  91. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  92. django_cfg/apps/payments/urls.py +46 -47
  93. django_cfg/apps/payments/urls_admin.py +49 -0
  94. django_cfg/apps/payments/views/api/__init__.py +101 -0
  95. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  96. django_cfg/apps/payments/views/api/balances.py +381 -0
  97. django_cfg/apps/payments/views/api/base.py +298 -0
  98. django_cfg/apps/payments/views/api/currencies.py +402 -0
  99. django_cfg/apps/payments/views/api/payments.py +415 -0
  100. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  101. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  102. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  103. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  104. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  105. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  106. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  107. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  108. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  109. django_cfg/apps/tasks/urls.py +0 -2
  110. django_cfg/apps/tasks/urls_admin.py +14 -0
  111. django_cfg/apps/urls.py +4 -4
  112. django_cfg/config.py +1 -1
  113. django_cfg/core/config.py +75 -4
  114. django_cfg/core/generation.py +25 -4
  115. django_cfg/core/integration/README.md +363 -0
  116. django_cfg/core/integration/__init__.py +47 -0
  117. django_cfg/core/integration/commands_collector.py +239 -0
  118. django_cfg/core/integration/display/__init__.py +15 -0
  119. django_cfg/core/integration/display/base.py +157 -0
  120. django_cfg/core/integration/display/ngrok.py +164 -0
  121. django_cfg/core/integration/display/startup.py +815 -0
  122. django_cfg/core/integration/url_integration.py +123 -0
  123. django_cfg/core/integration/version_checker.py +160 -0
  124. django_cfg/management/commands/auto_generate.py +4 -0
  125. django_cfg/management/commands/check_settings.py +6 -0
  126. django_cfg/management/commands/clear_constance.py +5 -2
  127. django_cfg/management/commands/create_token.py +6 -0
  128. django_cfg/management/commands/list_urls.py +6 -0
  129. django_cfg/management/commands/migrate_all.py +6 -0
  130. django_cfg/management/commands/migrator.py +3 -0
  131. django_cfg/management/commands/rundramatiq.py +6 -0
  132. django_cfg/management/commands/runserver_ngrok.py +51 -29
  133. django_cfg/management/commands/script.py +6 -0
  134. django_cfg/management/commands/show_config.py +12 -2
  135. django_cfg/management/commands/show_urls.py +4 -0
  136. django_cfg/management/commands/superuser.py +6 -0
  137. django_cfg/management/commands/task_clear.py +4 -1
  138. django_cfg/management/commands/task_status.py +3 -1
  139. django_cfg/management/commands/test_email.py +3 -0
  140. django_cfg/management/commands/test_telegram.py +6 -0
  141. django_cfg/management/commands/test_twilio.py +6 -0
  142. django_cfg/management/commands/tree.py +6 -0
  143. django_cfg/management/commands/validate_config.py +155 -149
  144. django_cfg/models/constance.py +31 -11
  145. django_cfg/models/payments.py +175 -498
  146. django_cfg/modules/django_currency/__init__.py +16 -11
  147. django_cfg/modules/django_currency/clients/__init__.py +4 -4
  148. django_cfg/modules/django_currency/clients/coinpaprika_client.py +289 -0
  149. django_cfg/modules/django_currency/clients/yahoo_client.py +157 -0
  150. django_cfg/modules/django_currency/core/__init__.py +1 -7
  151. django_cfg/modules/django_currency/core/converter.py +18 -23
  152. django_cfg/modules/django_currency/core/models.py +122 -11
  153. django_cfg/modules/django_currency/database/__init__.py +4 -4
  154. django_cfg/modules/django_currency/database/database_loader.py +190 -309
  155. django_cfg/modules/django_logger.py +160 -146
  156. django_cfg/modules/django_unfold/dashboard.py +65 -12
  157. django_cfg/registry/core.py +1 -0
  158. django_cfg/template_archive/django_sample.zip +0 -0
  159. django_cfg/templates/admin/components/action_grid.html +9 -9
  160. django_cfg/templates/admin/components/metric_card.html +5 -5
  161. django_cfg/templates/admin/components/status_badge.html +2 -2
  162. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +152 -24
  163. django_cfg/templates/admin/snippets/components/quick_actions.html +3 -3
  164. django_cfg/templates/admin/snippets/components/system_health.html +1 -1
  165. django_cfg/templates/admin/snippets/tabs/overview_tab.html +49 -52
  166. django_cfg/utils/smart_defaults.py +222 -571
  167. django_cfg/utils/toolkit.py +51 -11
  168. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/METADATA +5 -4
  169. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/RECORD +172 -182
  170. django_cfg/apps/payments/__init__.py +0 -8
  171. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  172. django_cfg/apps/payments/config/module.py +0 -70
  173. django_cfg/apps/payments/config/providers.py +0 -105
  174. django_cfg/apps/payments/config/settings.py +0 -96
  175. django_cfg/apps/payments/config/utils.py +0 -52
  176. django_cfg/apps/payments/decorators.py +0 -291
  177. django_cfg/apps/payments/management/commands/README.md +0 -178
  178. django_cfg/apps/payments/management/commands/currency_stats.py +0 -323
  179. django_cfg/apps/payments/management/commands/populate_currencies.py +0 -246
  180. django_cfg/apps/payments/management/commands/update_currencies.py +0 -336
  181. django_cfg/apps/payments/managers/__init__.py +0 -22
  182. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  183. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  184. django_cfg/apps/payments/managers/currency_manager.py +0 -83
  185. django_cfg/apps/payments/managers/payment_manager.py +0 -44
  186. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  187. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  188. django_cfg/apps/payments/models/events.py +0 -73
  189. django_cfg/apps/payments/serializers/__init__.py +0 -56
  190. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  191. django_cfg/apps/payments/serializers/balance.py +0 -59
  192. django_cfg/apps/payments/serializers/currencies.py +0 -55
  193. django_cfg/apps/payments/serializers/payments.py +0 -62
  194. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  195. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  196. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  197. django_cfg/apps/payments/services/cache/base.py +0 -30
  198. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  199. django_cfg/apps/payments/services/internal_types.py +0 -297
  200. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  201. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  202. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -222
  203. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  204. django_cfg/apps/payments/services/providers/cryptapi.py +0 -273
  205. django_cfg/apps/payments/services/providers/cryptomus.py +0 -311
  206. django_cfg/apps/payments/services/security/__init__.py +0 -34
  207. django_cfg/apps/payments/services/security/error_handler.py +0 -637
  208. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  209. django_cfg/apps/payments/services/security/webhook_validator.py +0 -475
  210. django_cfg/apps/payments/services/validators/__init__.py +0 -8
  211. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  212. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  213. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  214. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  215. django_cfg/apps/payments/tasks/__init__.py +0 -12
  216. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  217. django_cfg/apps/payments/templates/payments/base.html +0 -182
  218. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  219. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  220. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -36
  221. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  222. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -27
  223. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -144
  224. django_cfg/apps/payments/templates/payments/dashboard.html +0 -346
  225. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  226. django_cfg/apps/payments/urls_templates.py +0 -52
  227. django_cfg/apps/payments/utils/__init__.py +0 -45
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -245
  230. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  231. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  232. django_cfg/apps/payments/views/__init__.py +0 -62
  233. django_cfg/apps/payments/views/api_key_views.py +0 -164
  234. django_cfg/apps/payments/views/balance_views.py +0 -75
  235. django_cfg/apps/payments/views/currency_views.py +0 -111
  236. django_cfg/apps/payments/views/payment_views.py +0 -149
  237. django_cfg/apps/payments/views/subscription_views.py +0 -135
  238. django_cfg/apps/payments/views/tariff_views.py +0 -131
  239. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  240. django_cfg/apps/payments/views/templates/ajax.py +0 -312
  241. django_cfg/apps/payments/views/templates/base.py +0 -204
  242. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  243. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  244. django_cfg/apps/payments/views/templates/payment_management.py +0 -164
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -240
  247. django_cfg/apps/payments/views/templates/utils.py +0 -181
  248. django_cfg/apps/payments/views/webhook_views.py +0 -266
  249. django_cfg/apps/payments/viewsets.py +0 -65
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/modules/django_currency/clients/coingecko_client.py +0 -257
  252. django_cfg/modules/django_currency/clients/yfinance_client.py +0 -246
  253. django_cfg/template_archive/.gitignore +0 -1
  254. django_cfg/template_archive/__init__.py +0 -0
  255. django_cfg/urls.py +0 -33
  256. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  257. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  258. {django_cfg-1.2.29.dist-info → django_cfg-1.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,123 @@
1
+ """
2
+ Django CFG URL integration utilities.
3
+
4
+ Provides automatic URL registration for django_cfg endpoints and integrations.
5
+ """
6
+
7
+ from typing import List
8
+ from django.urls import path, include, URLPattern
9
+ from django_cfg.core.environment import EnvironmentDetector
10
+ from django.conf import settings
11
+
12
+
13
+ def add_django_cfg_urls(urlpatterns: List[URLPattern], cfg_prefix: str = "cfg/") -> List[URLPattern]:
14
+ """
15
+ Automatically add django_cfg URLs and all integrations to the main URL configuration.
16
+
17
+ This function adds:
18
+ - Django CFG management URLs (cfg/)
19
+ - Django Revolution URLs (if available)
20
+ - Startup information display (based on config)
21
+
22
+ Args:
23
+ urlpatterns: Existing URL patterns list
24
+ cfg_prefix: URL prefix for django_cfg endpoints (default: "cfg/")
25
+
26
+ Returns:
27
+ Updated URL patterns list with all URLs added
28
+
29
+ Example:
30
+ # In your main urls.py
31
+ from django_cfg import add_django_cfg_urls
32
+
33
+ urlpatterns = [
34
+ path("", home_view, name="home"),
35
+ path("admin/", admin.site.urls),
36
+ ]
37
+
38
+ # Automatically adds:
39
+ # - path("cfg/", include("django_cfg.apps.urls"))
40
+ # - Django Revolution URLs (if available)
41
+ # - Startup info display (based on config.startup_info_mode)
42
+ urlpatterns = add_django_cfg_urls(urlpatterns)
43
+ """
44
+ # Add django_cfg API URLs
45
+ new_patterns = urlpatterns + [
46
+ path(cfg_prefix, include("django_cfg.apps.urls")),
47
+ ]
48
+
49
+ # Try to add Django Revolution URLs if available
50
+ try:
51
+ from django_revolution import add_revolution_urls
52
+ new_patterns = add_revolution_urls(new_patterns)
53
+ except ImportError:
54
+ # Django Revolution not available - skip
55
+ pass
56
+
57
+ # Show startup info based on config
58
+ try:
59
+ from . import print_startup_info
60
+ print_startup_info()
61
+ except ImportError:
62
+ pass
63
+
64
+ return new_patterns
65
+
66
+
67
+ def get_django_cfg_urls_info() -> dict:
68
+ """
69
+ Get information about django_cfg URL integration and all integrations.
70
+
71
+ Returns:
72
+ Dictionary with complete URL integration info
73
+ """
74
+ from django_cfg.config import (
75
+ LIB_NAME,
76
+ LIB_SITE_URL,
77
+ LIB_DOCS_URL,
78
+ LIB_GITHUB_URL,
79
+ LIB_SUPPORT_URL,
80
+ LIB_HEALTH_URL,
81
+ )
82
+
83
+ try:
84
+ from django_cfg import __version__
85
+ version = __version__
86
+ except ImportError:
87
+ version = "unknown"
88
+
89
+ # Get current config directly from Pydantic
90
+ config = None
91
+ try:
92
+ from django_cfg.core.config import get_current_config
93
+ config = get_current_config()
94
+ except Exception:
95
+ pass
96
+
97
+
98
+ info = {
99
+ "django_cfg": {
100
+ "version": version,
101
+ "prefix": "cfg/",
102
+ "description": LIB_NAME,
103
+ "site_url": LIB_SITE_URL,
104
+ "docs_url": LIB_DOCS_URL,
105
+ "github_url": LIB_GITHUB_URL,
106
+ "support_url": LIB_SUPPORT_URL,
107
+ "health_url": LIB_HEALTH_URL,
108
+ "env_mode": config.env_mode if config else "unknown",
109
+ "debug": config.debug if config else False,
110
+ "startup_info_mode": config.startup_info_mode if config else "full",
111
+ }
112
+ }
113
+
114
+ # Add Django Revolution info if available
115
+ try:
116
+ from django_revolution import get_revolution_urls_info
117
+ revolution_info = get_revolution_urls_info()
118
+ if revolution_info:
119
+ info["django_revolution"] = revolution_info
120
+ except ImportError:
121
+ pass
122
+
123
+ return info
@@ -0,0 +1,160 @@
1
+ """
2
+ Version checker for django-cfg library.
3
+
4
+ Checks PyPI for the latest version and caches results for 24 hours using cachetools.
5
+ """
6
+
7
+ import importlib.metadata
8
+ import requests
9
+ from typing import Optional, Dict, Any
10
+ from cachetools import TTLCache
11
+
12
+
13
+ class VersionChecker:
14
+ """
15
+ Checks for library updates on PyPI with 24-hour caching using cachetools.
16
+ """
17
+
18
+ # Cache for 24 hours (86400 seconds)
19
+ CACHE_TTL = 86400
20
+
21
+ def __init__(self):
22
+ """Initialize version checker with TTL cache."""
23
+ self._cache = TTLCache(maxsize=10, ttl=self.CACHE_TTL)
24
+
25
+ def get_package_name(self) -> str:
26
+ """
27
+ Get package name automatically using importlib.metadata.
28
+
29
+ Returns:
30
+ Package name for PyPI
31
+ """
32
+ try:
33
+ # Get package metadata
34
+ metadata = importlib.metadata.metadata('django-cfg')
35
+ return metadata['Name']
36
+ except importlib.metadata.PackageNotFoundError:
37
+ # Fallback to hardcoded name
38
+ return 'django-cfg'
39
+
40
+ def get_latest_version(self) -> Optional[str]:
41
+ """
42
+ Get latest version from PyPI with caching.
43
+
44
+ Returns:
45
+ Latest version string or None if unavailable
46
+ """
47
+ # Check cache first
48
+ cached_version = self._cache.get('latest_version')
49
+ if cached_version:
50
+ return cached_version
51
+
52
+ try:
53
+ package_name = self.get_package_name()
54
+ url = f"https://pypi.org/pypi/{package_name}/json"
55
+
56
+ response = requests.get(url, timeout=5)
57
+ response.raise_for_status()
58
+
59
+ data = response.json()
60
+ version = data["info"]["version"]
61
+
62
+ # Cache the result
63
+ self._cache['latest_version'] = version
64
+
65
+ return version
66
+
67
+ except Exception:
68
+ # Silently fail - version checking is not critical
69
+ return None
70
+
71
+ def get_current_version(self) -> Optional[str]:
72
+ """
73
+ Get current installed version using importlib.metadata.
74
+
75
+ Returns:
76
+ Current version string or None if unavailable
77
+ """
78
+ try:
79
+ # First try to get from __version__
80
+ from django_cfg import __version__
81
+ return __version__
82
+ except ImportError:
83
+ try:
84
+ # Fallback to importlib.metadata
85
+ return importlib.metadata.version('django-cfg')
86
+ except importlib.metadata.PackageNotFoundError:
87
+ return None
88
+
89
+ def check_for_updates(self) -> Dict[str, Any]:
90
+ """
91
+ Check if an update is available.
92
+
93
+ Returns:
94
+ Dictionary with update information
95
+ """
96
+ current = self.get_current_version()
97
+ latest = self.get_latest_version()
98
+
99
+ result = {
100
+ 'current_version': current,
101
+ 'latest_version': latest,
102
+ 'update_available': False,
103
+ 'update_url': None,
104
+ }
105
+
106
+ if current and latest and current != latest:
107
+ # Simple version comparison (assumes semantic versioning)
108
+ try:
109
+ current_parts = [int(x) for x in current.split('.')]
110
+ latest_parts = [int(x) for x in latest.split('.')]
111
+
112
+ # Pad shorter version with zeros
113
+ max_len = max(len(current_parts), len(latest_parts))
114
+ current_parts.extend([0] * (max_len - len(current_parts)))
115
+ latest_parts.extend([0] * (max_len - len(latest_parts)))
116
+
117
+ if latest_parts > current_parts:
118
+ result['update_available'] = True
119
+ package_name = self.get_package_name()
120
+ result['update_url'] = f"https://pypi.org/project/{package_name}/"
121
+
122
+ except (ValueError, AttributeError):
123
+ # Version comparison failed - skip update check
124
+ pass
125
+
126
+ return result
127
+
128
+
129
+ # Global instance
130
+ _version_checker = VersionChecker()
131
+
132
+
133
+ def get_version_info() -> Dict[str, Any]:
134
+ """
135
+ Get version information and update status.
136
+
137
+ Returns:
138
+ Dictionary with version and update information
139
+ """
140
+ return _version_checker.check_for_updates()
141
+
142
+
143
+ def get_latest_version() -> Optional[str]:
144
+ """
145
+ Get latest version from PyPI.
146
+
147
+ Returns:
148
+ Latest version string or None if unavailable
149
+ """
150
+ return _version_checker.get_latest_version()
151
+
152
+
153
+ def get_current_version() -> Optional[str]:
154
+ """
155
+ Get current installed version.
156
+
157
+ Returns:
158
+ Current version string or None if unavailable
159
+ """
160
+ return _version_checker.get_current_version()
@@ -11,9 +11,12 @@ from django.core.management import call_command
11
11
  from django.conf import settings
12
12
  import questionary
13
13
  from datetime import datetime
14
+ from django_cfg.modules.django_logger import get_logger
14
15
 
15
16
  from django_cfg import ConfigToolkit
16
17
 
18
+ logger = get_logger('auto_generate')
19
+
17
20
 
18
21
  class Command(BaseCommand):
19
22
  help = 'Auto-generate configuration files and Django components'
@@ -36,6 +39,7 @@ class Command(BaseCommand):
36
39
  )
37
40
 
38
41
  def handle(self, *args, **options):
42
+ logger.info("Starting auto_generate command")
39
43
  if options['all']:
40
44
  self.generate_all()
41
45
  elif options['config']:
@@ -13,11 +13,16 @@ from django.conf import settings
13
13
  from django.core.exceptions import ImproperlyConfigured
14
14
  from django.core.mail import get_connection
15
15
  from datetime import datetime
16
+ from django_cfg.modules.django_logger import get_logger
16
17
 
17
18
 
19
+
20
+ logger = get_logger('check_settings')
21
+
18
22
  class Command(BaseCommand):
19
23
  """Command to check and debug django-cfg settings."""
20
24
 
25
+
21
26
  help = "Check and debug django-cfg configuration settings"
22
27
 
23
28
  def add_arguments(self, parser):
@@ -34,6 +39,7 @@ class Command(BaseCommand):
34
39
 
35
40
  def handle(self, *args, **options):
36
41
  """Main command handler."""
42
+ logger.info("Starting check_settings command")
37
43
  self.stdout.write(self.style.SUCCESS("\n🔍 Django CFG Settings Checker\n"))
38
44
 
39
45
  # Show basic info
@@ -6,12 +6,14 @@ Clear Constance configuration cache and database records.
6
6
  from django.core.management.base import BaseCommand
7
7
  from django.core.cache import cache
8
8
  from django.conf import settings
9
- import logging
9
+ from django_cfg.modules.django_logger import get_logger
10
+
10
11
 
11
- logger = logging.getLogger(__name__)
12
12
 
13
13
 
14
14
  class Command(BaseCommand):
15
+ logger = get_logger('clear_constance')
16
+
15
17
  help = 'Clear Constance configuration cache and database records'
16
18
 
17
19
  def add_arguments(self, parser):
@@ -38,6 +40,7 @@ class Command(BaseCommand):
38
40
 
39
41
  def handle(self, *args, **options):
40
42
  """Handle the command execution."""
43
+ self.logger.info("Starting clear_constance command")
41
44
  self.stdout.write(self.style.SUCCESS('🧹 Constance Clear Tool - Django Config Toolkit\n'))
42
45
 
43
46
  # Check if constance is installed
@@ -12,13 +12,18 @@ from django.contrib.auth import get_user_model
12
12
  from django.conf import settings
13
13
  import questionary
14
14
  from datetime import datetime, timedelta
15
+ from django_cfg.modules.django_logger import get_logger
16
+
15
17
 
16
18
  from django_cfg import ConfigToolkit
17
19
 
18
20
  User = get_user_model()
19
21
 
20
22
 
23
+ logger = get_logger('create_token')
24
+
21
25
  class Command(BaseCommand):
26
+
22
27
  help = 'Create API tokens and authentication tokens'
23
28
 
24
29
  def add_arguments(self, parser):
@@ -46,6 +51,7 @@ class Command(BaseCommand):
46
51
  )
47
52
 
48
53
  def handle(self, *args, **options):
54
+ logger.info("Starting create_token command")
49
55
  if options['user'] and options['type']:
50
56
  self.create_token_for_user(
51
57
  username=options['user'],
@@ -9,6 +9,8 @@ import re
9
9
  from django.core.management.base import BaseCommand
10
10
  from django.urls import get_resolver
11
11
  from django.conf import settings
12
+ from django_cfg.modules.django_logger import get_logger
13
+
12
14
 
13
15
  # Rich imports for beautiful output
14
16
  from rich.console import Console
@@ -20,8 +22,11 @@ from rich.align import Align
20
22
  from django_cfg.core.config import get_current_config
21
23
 
22
24
 
25
+ logger = get_logger('list_urls')
26
+
23
27
  class Command(BaseCommand):
24
28
  """Command to display all available URLs in the project."""
29
+
25
30
  help = "Display all available URLs with Rich formatting"
26
31
 
27
32
  def __init__(self, *args, **kwargs):
@@ -53,6 +58,7 @@ class Command(BaseCommand):
53
58
  )
54
59
 
55
60
  def handle(self, *args, **options):
61
+ logger.info("Starting list_urls command")
56
62
  filter_str = options["filter"]
57
63
  webhook_only = options["webhook"]
58
64
  api_only = options["api"]
@@ -6,11 +6,16 @@ Migrate all databases based on django-cfg configuration.
6
6
  from django.core.management.base import BaseCommand
7
7
  from django.core.management import call_command
8
8
  from django.apps import apps
9
+ from django_cfg.modules.django_logger import get_logger
10
+
9
11
 
10
12
  from django_cfg.core.config import get_current_config
11
13
 
12
14
 
15
+ logger = get_logger('migrate_all')
16
+
13
17
  class Command(BaseCommand):
18
+
14
19
  help = "Migrate all databases based on django-cfg configuration"
15
20
 
16
21
  def add_arguments(self, parser):
@@ -27,6 +32,7 @@ class Command(BaseCommand):
27
32
 
28
33
  def handle(self, *args, **options):
29
34
  """Run migrations for all configured databases."""
35
+ logger.info("Starting migrate_all command")
30
36
  dry_run = options.get("dry_run", False)
31
37
  skip_makemigrations = options.get("skip_makemigrations", False)
32
38
 
@@ -14,6 +14,9 @@ from django.conf import settings
14
14
  import questionary
15
15
  from datetime import datetime
16
16
  from django_cfg.core.config import DEFAULT_APPS
17
+ from django_cfg.modules.django_logger import get_logger
18
+
19
+ logger = get_logger('migrator')
17
20
 
18
21
 
19
22
  class Command(BaseCommand):
@@ -15,6 +15,8 @@ from django.apps import apps
15
15
  from django.conf import settings
16
16
  from django.core.management.base import BaseCommand
17
17
  from django.utils.module_loading import module_has_submodule
18
+ from django_cfg.modules.django_logger import get_logger
19
+
18
20
 
19
21
  from django_cfg.modules.django_tasks import get_task_service
20
22
 
@@ -24,6 +26,9 @@ NPROCS = multiprocessing.cpu_count()
24
26
  NTHREADS = 8
25
27
 
26
28
 
29
+ logger = get_logger('rundramatiq')
30
+
31
+
27
32
  class Command(BaseCommand):
28
33
  help = "Run Dramatiq workers with Django-CFG configuration."
29
34
 
@@ -77,6 +82,7 @@ class Command(BaseCommand):
77
82
 
78
83
  def handle(self, watch_dir, processes, threads, verbosity, queues,
79
84
  pid_file, log_file, worker_shutdown_timeout, dry_run, **options):
85
+ logger.info("Starting rundramatiq command")
80
86
 
81
87
  # Get task service and validate
82
88
  task_service = get_task_service()
@@ -5,11 +5,12 @@ Simple implementation following KISS principle.
5
5
  """
6
6
 
7
7
  import os
8
+ import time
8
9
  from django.core.management.commands.runserver import Command as RunServerCommand
9
10
  from django_cfg.modules.django_ngrok import get_ngrok_service
10
- import logging
11
+ from django_cfg.modules.django_logger import get_logger
11
12
 
12
- logger = logging.getLogger(__name__)
13
+ logger = get_logger('runserver_ngrok')
13
14
 
14
15
 
15
16
  class Command(RunServerCommand):
@@ -66,36 +67,61 @@ class Command(RunServerCommand):
66
67
 
67
68
  # Start ngrok tunnel
68
69
  ngrok_service = get_ngrok_service()
70
+
71
+ self.stdout.write("🚇 Starting ngrok tunnel...")
72
+ logger.info(f"Starting ngrok tunnel for port {server_port}")
73
+
69
74
  tunnel_url = ngrok_service.start_tunnel(server_port)
70
75
 
71
76
  if tunnel_url:
72
- self.stdout.write(
73
- self.style.SUCCESS(
74
- f"ngrok forwarding to http://127.0.0.1:{server_port} "
75
- f"from ingress url: {tunnel_url}"
76
- )
77
- )
77
+ # Wait for tunnel to be fully established
78
+ self.stdout.write("⏳ Waiting for tunnel to be established...")
79
+ logger.info("Waiting for ngrok tunnel to be fully established")
78
80
 
79
- # Set environment variables for ngrok URL
80
- self._set_ngrok_env_vars(tunnel_url)
81
+ max_retries = 10
82
+ retry_count = 0
83
+ tunnel_ready = False
81
84
 
82
- # Update ALLOWED_HOSTS if needed
83
- self._update_allowed_hosts(tunnel_url)
85
+ while retry_count < max_retries and not tunnel_ready:
86
+ time.sleep(1)
87
+ retry_count += 1
88
+
89
+ # Check if tunnel is actually accessible
90
+ try:
91
+ current_url = ngrok_service.get_tunnel_url()
92
+ if current_url and current_url == tunnel_url:
93
+ tunnel_ready = True
94
+ logger.info(f"Ngrok tunnel established successfully: {tunnel_url}")
95
+ break
96
+ except Exception as e:
97
+ logger.warning(f"Tunnel check attempt {retry_count} failed: {e}")
98
+
99
+ self.stdout.write(f"⏳ Tunnel check {retry_count}/{max_retries}...")
84
100
 
85
- # Update config URLs if enabled
86
- ngrok_service.update_config_urls()
87
-
88
- # Show webhook URL example
89
- webhook_url = ngrok_service.get_webhook_url("/api/webhooks/")
90
- self.stdout.write(
91
- self.style.HTTP_INFO(
92
- f"Webhook URL example: {webhook_url}"
101
+ if tunnel_ready:
102
+ # Set environment variables for ngrok URL
103
+ self._set_ngrok_env_vars(tunnel_url)
104
+
105
+ # Update ALLOWED_HOSTS if needed
106
+ self._update_allowed_hosts(tunnel_url)
107
+
108
+ # Update config URLs if enabled
109
+ ngrok_service.update_config_urls()
110
+
111
+ # Brief success message - detailed info will be shown by startup_display
112
+ self.stdout.write(
113
+ self.style.SUCCESS(f"✅ Ngrok tunnel ready: {tunnel_url}")
114
+ )
115
+ logger.info(f"Ngrok tunnel fully ready: {tunnel_url}")
116
+ else:
117
+ self.stdout.write(
118
+ self.style.WARNING("⚠️ Ngrok tunnel started but may not be fully ready")
93
119
  )
94
- )
120
+ logger.warning("Ngrok tunnel started but readiness check failed")
95
121
  else:
96
- self.stdout.write(
97
- self.style.WARNING("Failed to start ngrok tunnel")
98
- )
122
+ error_msg = "Failed to start ngrok tunnel"
123
+ self.stdout.write(self.style.ERROR(f" {error_msg}"))
124
+ logger.error(error_msg)
99
125
 
100
126
  def _set_ngrok_env_vars(self, tunnel_url: str):
101
127
  """Set environment variables with ngrok URL for easy access."""
@@ -114,11 +140,7 @@ class Command(RunServerCommand):
114
140
  # Set API URL (same as tunnel URL for most cases)
115
141
  os.environ['NGROK_API_URL'] = tunnel_url
116
142
 
117
- self.stdout.write(
118
- self.style.HTTP_INFO(
119
- f"Environment variables set: NGROK_URL={tunnel_url}"
120
- )
121
- )
143
+ # Environment variables set - no need for verbose output
122
144
  logger.info(f"Set ngrok environment variables: {tunnel_url}")
123
145
 
124
146
  except Exception as e:
@@ -13,11 +13,16 @@ from django.core.management import call_command
13
13
  from django.conf import settings
14
14
  import questionary
15
15
  from datetime import datetime
16
+ from django_cfg.modules.django_logger import get_logger
17
+
16
18
 
17
19
  from django_cfg import ConfigToolkit
18
20
 
19
21
 
22
+ logger = get_logger('script')
23
+
20
24
  class Command(BaseCommand):
25
+
21
26
  help = 'Run custom scripts and manage Django applications'
22
27
 
23
28
  def add_arguments(self, parser):
@@ -48,6 +53,7 @@ class Command(BaseCommand):
48
53
  )
49
54
 
50
55
  def handle(self, *args, **options):
56
+ logger.info("Starting script command")
51
57
  if options['list']:
52
58
  self.list_scripts()
53
59
  elif options['create']:
@@ -10,6 +10,9 @@ import json
10
10
  import os
11
11
  from django.core.management.base import BaseCommand
12
12
  from django.conf import settings
13
+ from django_cfg.modules.django_logger import get_logger
14
+
15
+ logger = get_logger('show_config')
13
16
 
14
17
 
15
18
  class Command(BaseCommand):
@@ -30,18 +33,25 @@ class Command(BaseCommand):
30
33
 
31
34
  def handle(self, *args, **options):
32
35
  """Show configuration in requested format."""
36
+ logger.info("Starting show_config command")
33
37
  try:
34
38
  # Get the config instance from Django settings
35
39
  config = self._get_config_instance()
40
+ logger.info("Successfully retrieved configuration instance")
36
41
 
37
42
  if options['format'] == 'json':
43
+ logger.info("Displaying configuration in JSON format")
38
44
  self._show_json_format(config, options['include_secrets'])
39
45
  else:
46
+ logger.info("Displaying configuration in table format")
40
47
  self._show_table_format(config, options['include_secrets'])
41
48
 
49
+ logger.info("show_config command completed successfully")
42
50
  except Exception as e:
51
+ error_msg = f'Failed to show configuration: {e}'
52
+ logger.error(error_msg, exc_info=True)
43
53
  self.stdout.write(
44
- self.style.ERROR(f'❌ Failed to show configuration: {e}')
54
+ self.style.ERROR(f'❌ {error_msg}')
45
55
  )
46
56
 
47
57
  def _get_config_instance(self):
@@ -92,7 +102,7 @@ class Command(BaseCommand):
92
102
  env_data = [
93
103
  ('Environment', getattr(config, '_environment', 'auto-detected')),
94
104
  ('Debug Mode', config.debug),
95
- ('Allowed Hosts', ', '.join(config.allowed_hosts)),
105
+ ('Security Domains', ', '.join(config.security_domains) if config.security_domains else 'None'),
96
106
  ]
97
107
 
98
108
  if include_secrets:
@@ -8,6 +8,9 @@ from django.core.management.base import BaseCommand, CommandParser
8
8
  from django.urls import get_resolver
9
9
  from django.conf import settings
10
10
  from django.utils.termcolors import make_style
11
+ from django_cfg.modules.django_logger import get_logger
12
+
13
+ logger = get_logger('show_urls')
11
14
 
12
15
 
13
16
  class Command(BaseCommand):
@@ -78,6 +81,7 @@ class Command(BaseCommand):
78
81
 
79
82
  def handle(self, *args, **options) -> None:
80
83
  """Main command handler."""
84
+ logger.info("Starting show_urls command")
81
85
  self.options = options
82
86
 
83
87
  # Disable colors if requested