django-cfg 1.2.31__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 (256) 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/manage_currencies.py +303 -151
  39. django_cfg/apps/payments/management/commands/manage_providers.py +333 -160
  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 +342 -152
  43. django_cfg/apps/payments/middleware/usage_tracking.py +249 -240
  44. django_cfg/apps/payments/migrations/0001_initial.py +708 -536
  45. django_cfg/apps/payments/models/__init__.py +13 -18
  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 +172 -148
  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 -285
  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 +346 -467
  67. django_cfg/apps/payments/services/core/subscription_service.py +425 -481
  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 +234 -174
  74. django_cfg/apps/payments/services/providers/nowpayments.py +478 -0
  75. django_cfg/apps/payments/services/providers/registry.py +367 -301
  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 +210 -129
  83. django_cfg/apps/payments/signals/balance_signals.py +174 -0
  84. django_cfg/apps/payments/signals/payment_signals.py +128 -103
  85. django_cfg/apps/payments/signals/subscription_signals.py +194 -142
  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 +45 -48
  93. django_cfg/apps/payments/urls_admin.py +33 -42
  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/config.py +1 -1
  110. django_cfg/core/config.py +40 -4
  111. django_cfg/core/generation.py +25 -4
  112. django_cfg/core/integration/README.md +363 -0
  113. django_cfg/core/integration/__init__.py +47 -0
  114. django_cfg/core/integration/commands_collector.py +239 -0
  115. django_cfg/core/integration/display/__init__.py +15 -0
  116. django_cfg/core/integration/display/base.py +157 -0
  117. django_cfg/core/integration/display/ngrok.py +164 -0
  118. django_cfg/core/integration/display/startup.py +815 -0
  119. django_cfg/core/integration/url_integration.py +123 -0
  120. django_cfg/core/integration/version_checker.py +160 -0
  121. django_cfg/management/commands/auto_generate.py +4 -0
  122. django_cfg/management/commands/check_settings.py +6 -0
  123. django_cfg/management/commands/clear_constance.py +5 -2
  124. django_cfg/management/commands/create_token.py +6 -0
  125. django_cfg/management/commands/list_urls.py +6 -0
  126. django_cfg/management/commands/migrate_all.py +6 -0
  127. django_cfg/management/commands/migrator.py +3 -0
  128. django_cfg/management/commands/rundramatiq.py +6 -0
  129. django_cfg/management/commands/runserver_ngrok.py +51 -29
  130. django_cfg/management/commands/script.py +6 -0
  131. django_cfg/management/commands/show_config.py +12 -2
  132. django_cfg/management/commands/show_urls.py +4 -0
  133. django_cfg/management/commands/superuser.py +6 -0
  134. django_cfg/management/commands/task_clear.py +4 -1
  135. django_cfg/management/commands/task_status.py +3 -1
  136. django_cfg/management/commands/test_email.py +3 -0
  137. django_cfg/management/commands/test_telegram.py +6 -0
  138. django_cfg/management/commands/test_twilio.py +6 -0
  139. django_cfg/management/commands/tree.py +6 -0
  140. django_cfg/management/commands/validate_config.py +155 -149
  141. django_cfg/models/constance.py +31 -11
  142. django_cfg/models/payments.py +175 -492
  143. django_cfg/modules/django_logger.py +160 -146
  144. django_cfg/modules/django_unfold/dashboard.py +64 -16
  145. django_cfg/registry/core.py +1 -0
  146. django_cfg/template_archive/django_sample.zip +0 -0
  147. django_cfg/utils/smart_defaults.py +222 -571
  148. django_cfg/utils/toolkit.py +51 -11
  149. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/METADATA +4 -1
  150. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/RECORD +153 -185
  151. django_cfg/apps/payments/__init__.py +0 -8
  152. django_cfg/apps/payments/admin/tariffs_admin.py +0 -199
  153. django_cfg/apps/payments/config/module.py +0 -70
  154. django_cfg/apps/payments/config/providers.py +0 -105
  155. django_cfg/apps/payments/config/settings.py +0 -96
  156. django_cfg/apps/payments/config/utils.py +0 -52
  157. django_cfg/apps/payments/decorators.py +0 -291
  158. django_cfg/apps/payments/management/commands/README.md +0 -146
  159. django_cfg/apps/payments/management/commands/currency_stats.py +0 -304
  160. django_cfg/apps/payments/managers/__init__.py +0 -23
  161. django_cfg/apps/payments/managers/api_key_manager.py +0 -35
  162. django_cfg/apps/payments/managers/balance_manager.py +0 -361
  163. django_cfg/apps/payments/managers/currency_manager.py +0 -306
  164. django_cfg/apps/payments/managers/payment_manager.py +0 -192
  165. django_cfg/apps/payments/managers/subscription_manager.py +0 -37
  166. django_cfg/apps/payments/managers/tariff_manager.py +0 -29
  167. django_cfg/apps/payments/migrations/0002_network_providercurrency_and_more.py +0 -241
  168. django_cfg/apps/payments/migrations/0003_add_usd_rate_cache.py +0 -30
  169. django_cfg/apps/payments/models/events.py +0 -73
  170. django_cfg/apps/payments/serializers/__init__.py +0 -57
  171. django_cfg/apps/payments/serializers/api_keys.py +0 -51
  172. django_cfg/apps/payments/serializers/balance.py +0 -59
  173. django_cfg/apps/payments/serializers/currencies.py +0 -63
  174. django_cfg/apps/payments/serializers/payments.py +0 -62
  175. django_cfg/apps/payments/serializers/subscriptions.py +0 -71
  176. django_cfg/apps/payments/serializers/tariffs.py +0 -56
  177. django_cfg/apps/payments/services/billing/__init__.py +0 -8
  178. django_cfg/apps/payments/services/cache/base.py +0 -30
  179. django_cfg/apps/payments/services/core/fallback_service.py +0 -432
  180. django_cfg/apps/payments/services/internal_types.py +0 -461
  181. django_cfg/apps/payments/services/middleware/__init__.py +0 -8
  182. django_cfg/apps/payments/services/monitoring/__init__.py +0 -22
  183. django_cfg/apps/payments/services/monitoring/api_schemas.py +0 -76
  184. django_cfg/apps/payments/services/monitoring/provider_health.py +0 -372
  185. django_cfg/apps/payments/services/providers/cryptapi/__init__.py +0 -4
  186. django_cfg/apps/payments/services/providers/cryptapi/config.py +0 -8
  187. django_cfg/apps/payments/services/providers/cryptapi/models.py +0 -192
  188. django_cfg/apps/payments/services/providers/cryptapi/provider.py +0 -439
  189. django_cfg/apps/payments/services/providers/cryptomus/__init__.py +0 -4
  190. django_cfg/apps/payments/services/providers/cryptomus/models.py +0 -176
  191. django_cfg/apps/payments/services/providers/cryptomus/provider.py +0 -429
  192. django_cfg/apps/payments/services/providers/cryptomus/provider_v2.py +0 -564
  193. django_cfg/apps/payments/services/providers/models/__init__.py +0 -34
  194. django_cfg/apps/payments/services/providers/models/currencies.py +0 -190
  195. django_cfg/apps/payments/services/providers/nowpayments/__init__.py +0 -4
  196. django_cfg/apps/payments/services/providers/nowpayments/models.py +0 -196
  197. django_cfg/apps/payments/services/providers/nowpayments/provider.py +0 -380
  198. django_cfg/apps/payments/services/providers/stripe/__init__.py +0 -4
  199. django_cfg/apps/payments/services/providers/stripe/models.py +0 -184
  200. django_cfg/apps/payments/services/providers/stripe/provider.py +0 -109
  201. django_cfg/apps/payments/services/security/__init__.py +0 -34
  202. django_cfg/apps/payments/services/security/error_handler.py +0 -635
  203. django_cfg/apps/payments/services/security/payment_notifications.py +0 -342
  204. django_cfg/apps/payments/services/security/webhook_validator.py +0 -474
  205. django_cfg/apps/payments/static/payments/css/payments.css +0 -340
  206. django_cfg/apps/payments/static/payments/js/notifications.js +0 -202
  207. django_cfg/apps/payments/static/payments/js/payment-utils.js +0 -318
  208. django_cfg/apps/payments/static/payments/js/theme.js +0 -86
  209. django_cfg/apps/payments/tasks/__init__.py +0 -12
  210. django_cfg/apps/payments/tasks/webhook_processing.py +0 -177
  211. django_cfg/apps/payments/templates/admin/payments/currency/change_list.html +0 -50
  212. django_cfg/apps/payments/templates/payments/base.html +0 -182
  213. django_cfg/apps/payments/templates/payments/components/payment_card.html +0 -201
  214. django_cfg/apps/payments/templates/payments/components/payment_qr_code.html +0 -109
  215. django_cfg/apps/payments/templates/payments/components/progress_bar.html +0 -43
  216. django_cfg/apps/payments/templates/payments/components/provider_stats.html +0 -40
  217. django_cfg/apps/payments/templates/payments/components/status_badge.html +0 -34
  218. django_cfg/apps/payments/templates/payments/components/status_overview.html +0 -148
  219. django_cfg/apps/payments/templates/payments/dashboard.html +0 -258
  220. django_cfg/apps/payments/templates/payments/dashboard_simple_test.html +0 -35
  221. django_cfg/apps/payments/templates/payments/payment_create.html +0 -579
  222. django_cfg/apps/payments/templates/payments/payment_detail.html +0 -373
  223. django_cfg/apps/payments/templates/payments/payment_list.html +0 -354
  224. django_cfg/apps/payments/templates/payments/stats.html +0 -261
  225. django_cfg/apps/payments/templates/payments/test.html +0 -213
  226. django_cfg/apps/payments/templatetags/payments_tags.py +0 -315
  227. django_cfg/apps/payments/utils/__init__.py +0 -43
  228. django_cfg/apps/payments/utils/billing_utils.py +0 -342
  229. django_cfg/apps/payments/utils/config_utils.py +0 -239
  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 -63
  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 -122
  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 -451
  241. django_cfg/apps/payments/views/templates/base.py +0 -212
  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 -158
  245. django_cfg/apps/payments/views/templates/qr_code.py +0 -174
  246. django_cfg/apps/payments/views/templates/stats.py +0 -244
  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 -66
  250. django_cfg/core/integration.py +0 -160
  251. django_cfg/template_archive/.gitignore +0 -1
  252. django_cfg/template_archive/__init__.py +0 -0
  253. django_cfg/urls.py +0 -33
  254. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/WHEEL +0 -0
  255. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.dist-info}/entry_points.txt +0 -0
  256. {django_cfg-1.2.31.dist-info → django_cfg-1.3.1.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