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.
Files changed (264) 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 -10
  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 +526 -222
  9. django_cfg/apps/payments/admin/filters.py +306 -199
  10. django_cfg/apps/payments/admin/payments_admin.py +465 -70
  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/cleanup_expired_data.py +419 -0
  39. django_cfg/apps/payments/management/commands/currency_stats.py +297 -225
  40. django_cfg/apps/payments/management/commands/manage_currencies.py +303 -151
  41. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  42. django_cfg/apps/payments/management/commands/process_pending_payments.py +357 -0
  43. django_cfg/apps/payments/management/commands/test_providers.py +434 -0
  44. django_cfg/apps/payments/middleware/__init__.py +3 -1
  45. django_cfg/apps/payments/middleware/api_access.py +329 -222
  46. django_cfg/apps/payments/middleware/rate_limiting.py +342 -152
  47. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  48. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  49. django_cfg/apps/payments/models/__init__.py +13 -18
  50. django_cfg/apps/payments/models/api_keys.py +121 -43
  51. django_cfg/apps/payments/models/balance.py +153 -115
  52. django_cfg/apps/payments/models/base.py +68 -15
  53. django_cfg/apps/payments/models/currencies.py +172 -148
  54. django_cfg/apps/payments/models/managers/__init__.py +44 -0
  55. django_cfg/apps/payments/models/managers/api_key_managers.py +329 -0
  56. django_cfg/apps/payments/models/managers/balance_managers.py +599 -0
  57. django_cfg/apps/payments/models/managers/currency_managers.py +385 -0
  58. django_cfg/apps/payments/models/managers/payment_managers.py +511 -0
  59. django_cfg/apps/payments/models/managers/subscription_managers.py +641 -0
  60. django_cfg/apps/payments/models/payments.py +235 -285
  61. django_cfg/apps/payments/models/subscriptions.py +257 -177
  62. django_cfg/apps/payments/models/tariffs.py +147 -40
  63. django_cfg/apps/payments/services/__init__.py +209 -56
  64. django_cfg/apps/payments/services/cache/__init__.py +6 -6
  65. django_cfg/apps/payments/services/cache_service/__init__.py +143 -0
  66. django_cfg/apps/payments/services/cache_service/api_key_cache.py +37 -0
  67. django_cfg/apps/payments/services/{cache/base.py → cache_service/interfaces.py} +3 -1
  68. django_cfg/apps/payments/services/cache_service/keys.py +49 -0
  69. django_cfg/apps/payments/services/cache_service/rate_limit_cache.py +47 -0
  70. django_cfg/apps/payments/services/cache_service/simple_cache.py +101 -0
  71. django_cfg/apps/payments/services/core/__init__.py +10 -6
  72. django_cfg/apps/payments/services/core/balance_service.py +435 -360
  73. django_cfg/apps/payments/services/core/base.py +166 -0
  74. django_cfg/apps/payments/services/core/currency_service.py +478 -0
  75. django_cfg/apps/payments/services/core/payment_service.py +371 -465
  76. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  77. django_cfg/apps/payments/services/core/webhook_service.py +410 -0
  78. django_cfg/apps/payments/services/integrations/__init__.py +29 -0
  79. django_cfg/apps/payments/services/integrations/ngrok_service.py +47 -0
  80. django_cfg/apps/payments/services/integrations/providers_config.py +107 -0
  81. django_cfg/apps/payments/services/providers/__init__.py +9 -14
  82. django_cfg/apps/payments/services/providers/base.py +234 -174
  83. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  84. django_cfg/apps/payments/services/providers/registry.py +367 -301
  85. django_cfg/apps/payments/services/types/__init__.py +78 -0
  86. django_cfg/apps/payments/services/types/data.py +177 -0
  87. django_cfg/apps/payments/services/types/requests.py +150 -0
  88. django_cfg/apps/payments/services/types/responses.py +156 -0
  89. django_cfg/apps/payments/services/types/webhooks.py +232 -0
  90. django_cfg/apps/payments/signals/__init__.py +33 -8
  91. django_cfg/apps/payments/signals/api_key_signals.py +210 -129
  92. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  93. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  94. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  95. django_cfg/apps/payments/static/payments/css/components.css +380 -0
  96. django_cfg/apps/payments/static/payments/css/dashboard.css +188 -0
  97. django_cfg/apps/payments/static/payments/js/components.js +545 -0
  98. django_cfg/apps/payments/static/payments/js/utils.js +412 -0
  99. django_cfg/apps/payments/templatetags/__init__.py +1 -1
  100. django_cfg/apps/payments/templatetags/payment_tags.py +466 -0
  101. django_cfg/apps/payments/urls.py +45 -48
  102. django_cfg/apps/payments/urls_admin.py +33 -42
  103. django_cfg/apps/payments/views/api/__init__.py +101 -0
  104. django_cfg/apps/payments/views/api/api_keys.py +387 -0
  105. django_cfg/apps/payments/views/api/balances.py +381 -0
  106. django_cfg/apps/payments/views/api/base.py +298 -0
  107. django_cfg/apps/payments/views/api/currencies.py +402 -0
  108. django_cfg/apps/payments/views/api/payments.py +415 -0
  109. django_cfg/apps/payments/views/api/subscriptions.py +475 -0
  110. django_cfg/apps/payments/views/api/webhooks.py +476 -0
  111. django_cfg/apps/payments/views/serializers/__init__.py +99 -0
  112. django_cfg/apps/payments/views/serializers/api_keys.py +424 -0
  113. django_cfg/apps/payments/views/serializers/balances.py +300 -0
  114. django_cfg/apps/payments/views/serializers/currencies.py +335 -0
  115. django_cfg/apps/payments/views/serializers/payments.py +387 -0
  116. django_cfg/apps/payments/views/serializers/subscriptions.py +429 -0
  117. django_cfg/apps/payments/views/serializers/webhooks.py +137 -0
  118. django_cfg/config.py +1 -1
  119. django_cfg/core/config.py +40 -4
  120. django_cfg/core/generation.py +25 -4
  121. django_cfg/core/integration/README.md +363 -0
  122. django_cfg/core/integration/__init__.py +47 -0
  123. django_cfg/core/integration/commands_collector.py +239 -0
  124. django_cfg/core/integration/display/__init__.py +15 -0
  125. django_cfg/core/integration/display/base.py +157 -0
  126. django_cfg/core/integration/display/ngrok.py +164 -0
  127. django_cfg/core/integration/display/startup.py +815 -0
  128. django_cfg/core/integration/url_integration.py +123 -0
  129. django_cfg/core/integration/version_checker.py +160 -0
  130. django_cfg/management/commands/auto_generate.py +4 -0
  131. django_cfg/management/commands/check_settings.py +6 -0
  132. django_cfg/management/commands/clear_constance.py +5 -2
  133. django_cfg/management/commands/create_token.py +6 -0
  134. django_cfg/management/commands/list_urls.py +6 -0
  135. django_cfg/management/commands/migrate_all.py +6 -0
  136. django_cfg/management/commands/migrator.py +3 -0
  137. django_cfg/management/commands/rundramatiq.py +6 -0
  138. django_cfg/management/commands/runserver_ngrok.py +51 -29
  139. django_cfg/management/commands/script.py +6 -0
  140. django_cfg/management/commands/show_config.py +12 -2
  141. django_cfg/management/commands/show_urls.py +4 -0
  142. django_cfg/management/commands/superuser.py +6 -0
  143. django_cfg/management/commands/task_clear.py +4 -1
  144. django_cfg/management/commands/task_status.py +3 -1
  145. django_cfg/management/commands/test_email.py +3 -0
  146. django_cfg/management/commands/test_telegram.py +6 -0
  147. django_cfg/management/commands/test_twilio.py +6 -0
  148. django_cfg/management/commands/tree.py +6 -0
  149. django_cfg/management/commands/validate_config.py +155 -149
  150. django_cfg/models/constance.py +31 -11
  151. django_cfg/models/payments.py +175 -492
  152. django_cfg/modules/django_logger.py +160 -146
  153. django_cfg/modules/django_unfold/dashboard.py +64 -16
  154. django_cfg/registry/core.py +1 -0
  155. django_cfg/template_archive/django_sample.zip +0 -0
  156. django_cfg/utils/smart_defaults.py +227 -570
  157. django_cfg/utils/toolkit.py +51 -11
  158. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/METADATA +4 -1
  159. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/RECORD +162 -185
  160. django_cfg/apps/payments/__init__.py +0 -8
  161. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  162. django_cfg/apps/payments/config/module.py +0 -70
  163. django_cfg/apps/payments/config/providers.py +0 -105
  164. django_cfg/apps/payments/config/settings.py +0 -96
  165. django_cfg/apps/payments/config/utils.py +0 -52
  166. django_cfg/apps/payments/decorators.py +0 -291
  167. django_cfg/apps/payments/management/commands/README.md +0 -146
  168. django_cfg/apps/payments/managers/__init__.py +0 -23
  169. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  170. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  171. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  172. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  173. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  174. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  175. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  176. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  177. django_cfg/apps/payments/models/events.py +0 -73
  178. django_cfg/apps/payments/serializers/__init__.py +0 -57
  179. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  180. django_cfg/apps/payments/serializers/balance.py +0 -59
  181. django_cfg/apps/payments/serializers/currencies.py +0 -63
  182. django_cfg/apps/payments/serializers/payments.py +0 -62
  183. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  184. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  185. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  186. django_cfg/apps/payments/services/cache/simple_cache.py +0 -135
  187. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  188. django_cfg/apps/payments/services/internal_types.py +0 -461
  189. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  190. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  191. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  192. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  193. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  194. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  195. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  196. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  197. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  198. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  199. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  200. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  201. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  202. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  203. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  204. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  205. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  206. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  207. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  208. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  209. django_cfg/apps/payments/services/security/__init__.py +0 -34
  210. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  211. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  212. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  213. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  214. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  215. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  216. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  217. django_cfg/apps/payments/tasks/__init__.py +0 -12
  218. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  219. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  220. django_cfg/apps/payments/templates/payments/base.html +0 -182
  221. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  222. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  223. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  224. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  225. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  226. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  227. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  228. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  229. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  230. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  231. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  232. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  233. django_cfg/apps/payments/templates/payments/test.html +0 -213
  234. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  235. django_cfg/apps/payments/utils/__init__.py +0 -43
  236. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  237. django_cfg/apps/payments/utils/config_utils.py +0 -239
  238. django_cfg/apps/payments/utils/middleware_utils.py +0 -228
  239. django_cfg/apps/payments/utils/validation_utils.py +0 -94
  240. django_cfg/apps/payments/views/__init__.py +0 -63
  241. django_cfg/apps/payments/views/api_key_views.py +0 -164
  242. django_cfg/apps/payments/views/balance_views.py +0 -75
  243. django_cfg/apps/payments/views/currency_views.py +0 -122
  244. django_cfg/apps/payments/views/payment_views.py +0 -149
  245. django_cfg/apps/payments/views/subscription_views.py +0 -135
  246. django_cfg/apps/payments/views/tariff_views.py +0 -131
  247. django_cfg/apps/payments/views/templates/__init__.py +0 -25
  248. django_cfg/apps/payments/views/templates/ajax.py +0 -451
  249. django_cfg/apps/payments/views/templates/base.py +0 -212
  250. django_cfg/apps/payments/views/templates/dashboard.py +0 -60
  251. django_cfg/apps/payments/views/templates/payment_detail.py +0 -102
  252. django_cfg/apps/payments/views/templates/payment_management.py +0 -158
  253. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  254. django_cfg/apps/payments/views/templates/stats.py +0 -244
  255. django_cfg/apps/payments/views/templates/utils.py +0 -181
  256. django_cfg/apps/payments/views/webhook_views.py +0 -266
  257. django_cfg/apps/payments/viewsets.py +0 -66
  258. django_cfg/core/integration.py +0 -160
  259. django_cfg/template_archive/.gitignore +0 -1
  260. django_cfg/template_archive/__init__.py +0 -0
  261. django_cfg/urls.py +0 -33
  262. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/WHEEL +0 -0
  263. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/entry_points.txt +0 -0
  264. {django_cfg-1.2.31.dist-info → django_cfg-1.3.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,157 @@
1
+ """
2
+ Base display manager for Django CFG startup information.
3
+ """
4
+
5
+ from typing import Optional, Dict, Any, List
6
+ from rich.console import Console
7
+ from rich.table import Table
8
+ from rich.panel import Panel
9
+ from rich.columns import Columns
10
+ from rich.text import Text
11
+ from rich.align import Align
12
+
13
+ # Panel width configuration - use fixed widths for consistent layout
14
+ CONSOLE_WIDTH = 120 # Console width that fits most terminals
15
+ MAIN_PANEL_WIDTH = CONSOLE_WIDTH # Full width panels
16
+ HALF_PANEL_WIDTH = (CONSOLE_WIDTH - 10) // 2 # 50% width for columns (minus borders/padding)
17
+
18
+
19
+ class BaseDisplayManager:
20
+ """Base class for all display managers."""
21
+
22
+ def __init__(self, config=None):
23
+ """Initialize display manager with config."""
24
+ self.config = config or self._get_current_config()
25
+ self.console = Console()
26
+
27
+ def _get_current_config(self):
28
+ """Get current Django CFG configuration."""
29
+ try:
30
+ from django_cfg.core.config import get_current_config
31
+ return get_current_config()
32
+ except Exception:
33
+ return None
34
+
35
+ def get_base_url(self, *paths) -> str:
36
+ """Get base URL for API endpoints with optional path components."""
37
+ if self.config and hasattr(self.config, 'api_url'):
38
+ base = self.config.api_url.rstrip('/')
39
+ else:
40
+ base = "http://localhost:8000"
41
+
42
+ if paths:
43
+ # Join all path components
44
+ path_parts = []
45
+ for path in paths:
46
+ if path:
47
+ path_parts.append(str(path).strip('/'))
48
+
49
+ if path_parts:
50
+ return f"{base}/{'/'.join(path_parts)}/"
51
+
52
+ return base
53
+
54
+ def get_environment_style(self):
55
+ """Get environment styling (panel_style, env_emoji, env_color)."""
56
+ if not self.config:
57
+ return "yellow", "🧪", "yellow"
58
+
59
+ if self.config.is_development:
60
+ return "green", "🚧", "green"
61
+ elif self.config.is_production:
62
+ return "red", "🚀", "red"
63
+ else:
64
+ return "yellow", "🧪", "yellow"
65
+
66
+ def get_version(self) -> str:
67
+ """Get Django CFG version."""
68
+ try:
69
+ from django_cfg import __version__
70
+ return __version__
71
+ except ImportError:
72
+ return "unknown"
73
+
74
+ def create_panel(self, content, title: str, border_style: str = "blue",
75
+ width: Optional[int] = None, expand: bool = False) -> Panel:
76
+ """Create a standardized panel with fixed width by default."""
77
+ # Use MAIN_PANEL_WIDTH by default for consistent layout
78
+ panel_width = width if width is not None else MAIN_PANEL_WIDTH
79
+
80
+ return Panel(
81
+ content,
82
+ title=title,
83
+ border_style=border_style,
84
+ width=panel_width,
85
+ expand=expand,
86
+ padding=(1, 2)
87
+ )
88
+
89
+ def create_full_width_panel(self, content, title: str, border_style: str = "blue") -> Panel:
90
+ """Create a panel that spans the full width (same as two columns)."""
91
+ # Wrap in a table to match the width of two-column layout
92
+ wrapper_table = Table(show_header=False, box=None, padding=(0, 0), width=MAIN_PANEL_WIDTH)
93
+ wrapper_table.add_column("Content", width=MAIN_PANEL_WIDTH, justify="left")
94
+
95
+ panel = Panel(
96
+ content,
97
+ title=title,
98
+ border_style=border_style,
99
+ expand=True,
100
+ padding=(1, 2)
101
+ )
102
+
103
+ wrapper_table.add_row(panel)
104
+ return wrapper_table
105
+
106
+ def create_table(self, title: str = None, show_header: bool = False) -> Table:
107
+ """Create a standardized table."""
108
+ table = Table(title=title, show_header=show_header, box=None)
109
+ return table
110
+
111
+ def print_panel(self, panel: Panel, centered: bool = False):
112
+ """Print a panel, optionally centered."""
113
+ if centered:
114
+ self.console.print(Align.center(panel))
115
+ else:
116
+ self.console.print(panel)
117
+
118
+ def print_columns(self, panels: List[Panel], equal: bool = True, expand: bool = True):
119
+ """Print panels in columns."""
120
+ if panels:
121
+ self.console.print(Columns(panels, equal=equal, expand=expand))
122
+
123
+ def print_two_column_table(self, left_content: str, right_content: str,
124
+ left_title: str = "", right_title: str = "",
125
+ left_style: str = "blue", right_style: str = "green"):
126
+ """Print content in a proper 50/50 two-column layout with panels."""
127
+ # Create panels that will expand to fill table cells
128
+ left_panel = Panel(
129
+ left_content,
130
+ title=left_title,
131
+ border_style=left_style,
132
+ expand=True,
133
+ padding=(1, 1)
134
+ )
135
+
136
+ right_panel = Panel(
137
+ right_content,
138
+ title=right_title,
139
+ border_style=right_style,
140
+ expand=True,
141
+ padding=(1, 1)
142
+ )
143
+
144
+ # Use a table to force exact positioning
145
+ wrapper_table = Table(show_header=False, box=None, padding=(0, 0), width=MAIN_PANEL_WIDTH)
146
+ wrapper_table.add_column("Left", width=HALF_PANEL_WIDTH, justify="left")
147
+ wrapper_table.add_column("Right", width=HALF_PANEL_WIDTH, justify="left")
148
+
149
+ # Add panels as table cells
150
+ wrapper_table.add_row(left_panel, right_panel)
151
+
152
+ self.console.print(wrapper_table)
153
+
154
+ def print_spacing(self, lines: int = 1):
155
+ """Print empty lines for spacing."""
156
+ for _ in range(lines):
157
+ self.console.print()
@@ -0,0 +1,164 @@
1
+ """
2
+ Ngrok display manager for Django CFG.
3
+ """
4
+
5
+ from typing import Optional
6
+ from .base import BaseDisplayManager, MAIN_PANEL_WIDTH
7
+
8
+
9
+ class NgrokDisplayManager(BaseDisplayManager):
10
+ """Manager for displaying ngrok tunnel information."""
11
+
12
+ def display_tunnel_info(self, tunnel_url: str):
13
+ """Display active ngrok tunnel information."""
14
+ try:
15
+ if not self._is_ngrok_configured():
16
+ return
17
+
18
+ ngrok_service = self._get_ngrok_service()
19
+ if not ngrok_service:
20
+ return
21
+
22
+ # Create ngrok info table
23
+ ngrok_table = self.create_table()
24
+ ngrok_table.add_column("Key", style="cyan", no_wrap=True)
25
+ ngrok_table.add_column("Value", style="bright_white")
26
+
27
+ # Add tunnel information
28
+ ngrok_table.add_row("🌐 Tunnel URL:", tunnel_url)
29
+
30
+ # Add webhook URL example
31
+ webhook_url = ngrok_service.get_webhook_url()
32
+ ngrok_table.add_row("🔗 Webhook URL:", webhook_url)
33
+
34
+ # Add API URL
35
+ api_url = ngrok_service.get_api_url()
36
+ ngrok_table.add_row("🚀 API URL:", api_url)
37
+
38
+ # Add environment variables info
39
+ ngrok_table.add_row("📝 Env Variables:", "NGROK_URL, DJANGO_NGROK_URL set")
40
+
41
+ # Add domain info if configured
42
+ if self.config.ngrok.tunnel.domain:
43
+ ngrok_table.add_row("🏷️ Custom Domain:", self.config.ngrok.tunnel.domain)
44
+
45
+ # Add auth info
46
+ if self.config.ngrok.auth.get_authtoken():
47
+ ngrok_table.add_row("🔐 Auth Token:", "✅ Configured")
48
+ else:
49
+ ngrok_table.add_row("🔐 Auth Token:", "❌ Not configured (limited features)")
50
+
51
+ # Create panel with ngrok info
52
+ ngrok_panel = self.create_panel(
53
+ ngrok_table,
54
+ title="🚇 [bold green]Ngrok Tunnel Active[/bold green]",
55
+ border_style="green",
56
+ width=MAIN_PANEL_WIDTH
57
+ )
58
+
59
+ # Print the panel (not centered to respect width)
60
+ self.print_spacing()
61
+ self.console.print(ngrok_panel)
62
+ self.print_spacing()
63
+
64
+ except Exception:
65
+ # Silently fail - ngrok info is not critical
66
+ pass
67
+
68
+ def display_config_status(self):
69
+ """Display ngrok configuration status when tunnel is not active."""
70
+ try:
71
+ if not self._is_ngrok_configured():
72
+ return
73
+
74
+ ngrok_service = self._get_ngrok_service()
75
+ if not ngrok_service:
76
+ return
77
+
78
+ # Create ngrok config status table
79
+ status_table = self.create_table()
80
+ status_table.add_column("Key", style="cyan", no_wrap=True)
81
+ status_table.add_column("Value", style="bright_white")
82
+
83
+ # Add configuration status
84
+ status_table.add_row("🔧 Configuration:", "✅ Enabled")
85
+
86
+ # Add auth status
87
+ if self.config.ngrok.auth.get_authtoken():
88
+ status_table.add_row("🔐 Auth Token:", "✅ Configured")
89
+ else:
90
+ status_table.add_row("🔐 Auth Token:", "❌ Not configured")
91
+
92
+ # Add domain info if configured
93
+ if self.config.ngrok.tunnel.domain:
94
+ status_table.add_row("🏷️ Custom Domain:", self.config.ngrok.tunnel.domain)
95
+
96
+ # Add auto-start status
97
+ if self.config.ngrok.auto_start:
98
+ status_table.add_row("🚀 Auto Start:", "✅ Enabled")
99
+ else:
100
+ status_table.add_row("🚀 Auto Start:", "❌ Disabled")
101
+
102
+ # Add usage hint
103
+ status_table.add_row("💡 Usage:", "Run 'pnpm manage.py runserver_ngrok' to start tunnel")
104
+
105
+ # Create panel with ngrok config status - full width
106
+ ngrok_panel = self.create_full_width_panel(
107
+ status_table,
108
+ title="🚇 [bold yellow]Ngrok Ready (Not Active)[/bold yellow]",
109
+ border_style="yellow"
110
+ )
111
+
112
+ # Print the panel (not centered to respect width)
113
+ self.print_spacing()
114
+ self.console.print(ngrok_panel)
115
+ self.print_spacing()
116
+
117
+ except Exception:
118
+ # Silently fail - ngrok config status is not critical
119
+ pass
120
+
121
+ def display_if_active(self):
122
+ """Display ngrok information if configured and check if active."""
123
+ if not self._is_ngrok_configured():
124
+ return
125
+
126
+ ngrok_service = self._get_ngrok_service()
127
+ if not ngrok_service:
128
+ return
129
+
130
+ # Check if tunnel is active or available from environment
131
+ tunnel_url = ngrok_service.get_tunnel_url()
132
+ env_url = ngrok_service.get_tunnel_url_from_env()
133
+
134
+ # IMPORTANT: During Django startup, ngrok tunnel may not be active yet
135
+ # Only show active tunnel info if we actually have a tunnel URL
136
+ # Otherwise, show config status (ready but not active)
137
+ active_url = None
138
+
139
+ if tunnel_url:
140
+ # Active tunnel found from manager
141
+ active_url = tunnel_url
142
+ elif env_url:
143
+ # Environment URL found (tunnel was started in this process)
144
+ active_url = env_url
145
+
146
+ if active_url:
147
+ self.display_tunnel_info(active_url)
148
+ else:
149
+ self.display_config_status()
150
+
151
+ def _is_ngrok_configured(self) -> bool:
152
+ """Check if ngrok is configured."""
153
+ return (self.config and
154
+ hasattr(self.config, 'ngrok') and
155
+ self.config.ngrok and
156
+ self.config.ngrok.enabled)
157
+
158
+ def _get_ngrok_service(self):
159
+ """Get ngrok service instance."""
160
+ try:
161
+ from django_cfg.modules.django_ngrok import get_ngrok_service
162
+ return get_ngrok_service()
163
+ except Exception:
164
+ return None