django-cfg 1.4.61__py3-none-any.whl → 1.4.63__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.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (179) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/services/otp_service.py +3 -14
  3. django_cfg/apps/centrifugo/__init__.py +57 -0
  4. django_cfg/apps/centrifugo/admin/__init__.py +13 -0
  5. django_cfg/apps/centrifugo/admin/centrifugo_log.py +249 -0
  6. django_cfg/apps/centrifugo/admin/config.py +82 -0
  7. django_cfg/apps/centrifugo/apps.py +31 -0
  8. django_cfg/apps/centrifugo/codegen/IMPLEMENTATION_SUMMARY.md +475 -0
  9. django_cfg/apps/centrifugo/codegen/README.md +242 -0
  10. django_cfg/apps/centrifugo/codegen/USAGE.md +616 -0
  11. django_cfg/apps/centrifugo/codegen/__init__.py +19 -0
  12. django_cfg/apps/centrifugo/codegen/discovery.py +246 -0
  13. django_cfg/apps/centrifugo/codegen/generators/go_thin/__init__.py +5 -0
  14. django_cfg/apps/centrifugo/codegen/generators/go_thin/generator.py +174 -0
  15. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/README.md.j2 +182 -0
  16. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/client.go.j2 +64 -0
  17. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/go.mod.j2 +10 -0
  18. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2 +300 -0
  19. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2.old +267 -0
  20. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/types.go.j2 +16 -0
  21. django_cfg/apps/centrifugo/codegen/generators/python_thin/__init__.py +7 -0
  22. django_cfg/apps/centrifugo/codegen/generators/python_thin/generator.py +241 -0
  23. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/README.md.j2 +128 -0
  24. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/__init__.py.j2 +22 -0
  25. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/client.py.j2 +73 -0
  26. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/models.py.j2 +19 -0
  27. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/requirements.txt.j2 +8 -0
  28. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/rpc_client.py.j2 +193 -0
  29. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/__init__.py +5 -0
  30. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/generator.py +124 -0
  31. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/README.md.j2 +38 -0
  32. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/client.ts.j2 +25 -0
  33. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/index.ts.j2 +12 -0
  34. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/package.json.j2 +13 -0
  35. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +137 -0
  36. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/tsconfig.json.j2 +14 -0
  37. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/types.ts.j2 +9 -0
  38. django_cfg/apps/centrifugo/codegen/utils/__init__.py +37 -0
  39. django_cfg/apps/centrifugo/codegen/utils/naming.py +155 -0
  40. django_cfg/apps/centrifugo/codegen/utils/type_converter.py +349 -0
  41. django_cfg/apps/centrifugo/decorators.py +137 -0
  42. django_cfg/apps/centrifugo/management/__init__.py +1 -0
  43. django_cfg/apps/centrifugo/management/commands/__init__.py +1 -0
  44. django_cfg/apps/centrifugo/management/commands/generate_centrifugo_clients.py +254 -0
  45. django_cfg/apps/centrifugo/managers/__init__.py +12 -0
  46. django_cfg/apps/centrifugo/managers/centrifugo_log.py +264 -0
  47. django_cfg/apps/centrifugo/migrations/0001_initial.py +164 -0
  48. django_cfg/apps/centrifugo/migrations/__init__.py +3 -0
  49. django_cfg/apps/centrifugo/models/__init__.py +11 -0
  50. django_cfg/apps/centrifugo/models/centrifugo_log.py +210 -0
  51. django_cfg/apps/centrifugo/registry.py +106 -0
  52. django_cfg/apps/centrifugo/router.py +125 -0
  53. django_cfg/apps/centrifugo/serializers/__init__.py +40 -0
  54. django_cfg/apps/centrifugo/serializers/admin_api.py +264 -0
  55. django_cfg/apps/centrifugo/serializers/channels.py +26 -0
  56. django_cfg/apps/centrifugo/serializers/health.py +17 -0
  57. django_cfg/apps/centrifugo/serializers/publishes.py +16 -0
  58. django_cfg/apps/centrifugo/serializers/stats.py +21 -0
  59. django_cfg/apps/centrifugo/services/__init__.py +12 -0
  60. django_cfg/apps/centrifugo/services/client/__init__.py +29 -0
  61. django_cfg/apps/centrifugo/services/client/client.py +577 -0
  62. django_cfg/apps/centrifugo/services/client/config.py +228 -0
  63. django_cfg/apps/centrifugo/services/client/exceptions.py +212 -0
  64. django_cfg/apps/centrifugo/services/config_helper.py +63 -0
  65. django_cfg/apps/centrifugo/services/dashboard_notifier.py +157 -0
  66. django_cfg/apps/centrifugo/services/logging.py +677 -0
  67. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +260 -0
  68. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +313 -0
  69. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +803 -0
  70. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +333 -0
  71. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +432 -0
  72. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +33 -0
  73. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +210 -0
  74. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +46 -0
  75. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +123 -0
  76. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +45 -0
  77. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +84 -0
  78. django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/stat_cards.html +23 -20
  79. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +91 -0
  80. django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/tab_navigation.html +15 -15
  81. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +415 -0
  82. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +61 -0
  83. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +58 -0
  84. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +48 -0
  85. django_cfg/apps/centrifugo/templatetags/__init__.py +1 -0
  86. django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +81 -0
  87. django_cfg/apps/centrifugo/urls.py +31 -0
  88. django_cfg/apps/{ipc → centrifugo}/urls_admin.py +4 -4
  89. django_cfg/apps/centrifugo/views/__init__.py +15 -0
  90. django_cfg/apps/centrifugo/views/admin_api.py +374 -0
  91. django_cfg/apps/centrifugo/views/dashboard.py +15 -0
  92. django_cfg/apps/centrifugo/views/monitoring.py +286 -0
  93. django_cfg/apps/centrifugo/views/testing_api.py +422 -0
  94. django_cfg/apps/support/utils/support_email_service.py +5 -18
  95. django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -2
  96. django_cfg/apps/urls.py +5 -5
  97. django_cfg/core/base/config_model.py +4 -44
  98. django_cfg/core/builders/apps_builder.py +2 -2
  99. django_cfg/core/generation/integration_generators/third_party.py +8 -8
  100. django_cfg/core/utils/__init__.py +5 -0
  101. django_cfg/core/utils/url_helpers.py +73 -0
  102. django_cfg/modules/base.py +7 -7
  103. django_cfg/modules/django_client/core/__init__.py +2 -1
  104. django_cfg/modules/django_client/core/config/config.py +8 -0
  105. django_cfg/modules/django_client/core/generator/__init__.py +42 -2
  106. django_cfg/modules/django_client/core/generator/go/__init__.py +14 -0
  107. django_cfg/modules/django_client/core/generator/go/client_generator.py +124 -0
  108. django_cfg/modules/django_client/core/generator/go/files_generator.py +133 -0
  109. django_cfg/modules/django_client/core/generator/go/generator.py +203 -0
  110. django_cfg/modules/django_client/core/generator/go/models_generator.py +304 -0
  111. django_cfg/modules/django_client/core/generator/go/naming.py +193 -0
  112. django_cfg/modules/django_client/core/generator/go/operations_generator.py +134 -0
  113. django_cfg/modules/django_client/core/generator/go/templates/Makefile.j2 +38 -0
  114. django_cfg/modules/django_client/core/generator/go/templates/README.md.j2 +55 -0
  115. django_cfg/modules/django_client/core/generator/go/templates/client.go.j2 +122 -0
  116. django_cfg/modules/django_client/core/generator/go/templates/enums.go.j2 +49 -0
  117. django_cfg/modules/django_client/core/generator/go/templates/errors.go.j2 +182 -0
  118. django_cfg/modules/django_client/core/generator/go/templates/go.mod.j2 +6 -0
  119. django_cfg/modules/django_client/core/generator/go/templates/main_client.go.j2 +60 -0
  120. django_cfg/modules/django_client/core/generator/go/templates/middleware.go.j2 +388 -0
  121. django_cfg/modules/django_client/core/generator/go/templates/models.go.j2 +28 -0
  122. django_cfg/modules/django_client/core/generator/go/templates/operations_client.go.j2 +142 -0
  123. django_cfg/modules/django_client/core/generator/go/templates/validation.go.j2 +217 -0
  124. django_cfg/modules/django_client/core/generator/go/type_mapper.py +380 -0
  125. django_cfg/modules/django_client/management/commands/generate_client.py +53 -3
  126. django_cfg/modules/django_client/system/generate_mjs_clients.py +3 -1
  127. django_cfg/modules/django_client/system/schema_parser.py +5 -1
  128. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +1 -0
  129. django_cfg/modules/django_twilio/sendgrid_service.py +7 -4
  130. django_cfg/modules/django_unfold/dashboard.py +25 -19
  131. django_cfg/pyproject.toml +1 -1
  132. django_cfg/registry/core.py +2 -0
  133. django_cfg/registry/modules.py +2 -2
  134. django_cfg/static/js/api/centrifugo/client.mjs +164 -0
  135. django_cfg/static/js/api/centrifugo/index.mjs +13 -0
  136. django_cfg/static/js/api/index.mjs +5 -5
  137. django_cfg/static/js/api/types.mjs +89 -26
  138. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/METADATA +1 -1
  139. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/RECORD +142 -68
  140. django_cfg/apps/ipc/README.md +0 -346
  141. django_cfg/apps/ipc/RPC_LOGGING.md +0 -321
  142. django_cfg/apps/ipc/TESTING.md +0 -539
  143. django_cfg/apps/ipc/__init__.py +0 -60
  144. django_cfg/apps/ipc/admin.py +0 -212
  145. django_cfg/apps/ipc/apps.py +0 -28
  146. django_cfg/apps/ipc/migrations/0001_initial.py +0 -137
  147. django_cfg/apps/ipc/migrations/__init__.py +0 -0
  148. django_cfg/apps/ipc/models.py +0 -221
  149. django_cfg/apps/ipc/serializers/__init__.py +0 -29
  150. django_cfg/apps/ipc/serializers/serializers.py +0 -343
  151. django_cfg/apps/ipc/services/__init__.py +0 -7
  152. django_cfg/apps/ipc/services/client/__init__.py +0 -23
  153. django_cfg/apps/ipc/services/client/client.py +0 -621
  154. django_cfg/apps/ipc/services/client/config.py +0 -214
  155. django_cfg/apps/ipc/services/client/exceptions.py +0 -201
  156. django_cfg/apps/ipc/services/logging.py +0 -239
  157. django_cfg/apps/ipc/services/monitor.py +0 -466
  158. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/main.mjs +0 -269
  159. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/overview.mjs +0 -259
  160. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/testing.mjs +0 -375
  161. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard.mjs.old +0 -441
  162. django_cfg/apps/ipc/templates/django_cfg_ipc/components/methods_content.html +0 -22
  163. django_cfg/apps/ipc/templates/django_cfg_ipc/components/notifications_content.html +0 -9
  164. django_cfg/apps/ipc/templates/django_cfg_ipc/components/overview_content.html +0 -9
  165. django_cfg/apps/ipc/templates/django_cfg_ipc/components/requests_content.html +0 -23
  166. django_cfg/apps/ipc/templates/django_cfg_ipc/components/system_status.html +0 -47
  167. django_cfg/apps/ipc/templates/django_cfg_ipc/components/testing_tools.html +0 -184
  168. django_cfg/apps/ipc/templates/django_cfg_ipc/layout/base.html +0 -71
  169. django_cfg/apps/ipc/templates/django_cfg_ipc/pages/dashboard.html +0 -56
  170. django_cfg/apps/ipc/urls.py +0 -23
  171. django_cfg/apps/ipc/views/__init__.py +0 -13
  172. django_cfg/apps/ipc/views/dashboard.py +0 -15
  173. django_cfg/apps/ipc/views/monitoring.py +0 -251
  174. django_cfg/apps/ipc/views/testing.py +0 -285
  175. django_cfg/static/js/api/ipc/client.mjs +0 -114
  176. django_cfg/static/js/api/ipc/index.mjs +0 -13
  177. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/WHEEL +0 -0
  178. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/entry_points.txt +0 -0
  179. {django_cfg-1.4.61.dist-info → django_cfg-1.4.63.dist-info}/licenses/LICENSE +0 -0
@@ -6,6 +6,7 @@ import logging
6
6
 
7
7
  from django.contrib.auth import get_user_model
8
8
 
9
+ from django_cfg.core.utils import get_ticket_url
9
10
  from django_cfg.modules.django_email import DjangoEmailService
10
11
 
11
12
  User = get_user_model()
@@ -18,20 +19,6 @@ class SupportEmailService:
18
19
  def __init__(self, user: User):
19
20
  self.user = user
20
21
 
21
- def _get_ticket_url(self, ticket_uuid: str) -> str:
22
- """Get ticket URL from configuration."""
23
- try:
24
- from django_cfg.core.state import get_current_config
25
- config = get_current_config()
26
- if config and hasattr(config, 'get_ticket_url'):
27
- return config.get_ticket_url(str(ticket_uuid))
28
- else:
29
- # Fallback URL if config is not available
30
- return f"#ticket-{ticket_uuid}"
31
- except Exception as e:
32
- logger.warning(f"Could not generate ticket URL: {e}")
33
- return f"#ticket-{ticket_uuid}"
34
-
35
22
  def _send_email(
36
23
  self,
37
24
  subject: str,
@@ -72,7 +59,7 @@ class SupportEmailService:
72
59
  main_html_content=f'<div style="background: #e8f5e8; padding: 15px; border-left: 4px solid #28a745; margin: 15px 0;"><strong>Ticket #{ticket.uuid.hex[:8]}</strong><br><strong>Subject:</strong> {ticket.subject}</div>',
73
60
  secondary_text="You will receive email notifications when our support team replies to your ticket.",
74
61
  button_text="View Ticket",
75
- button_url=self._get_ticket_url(ticket.uuid),
62
+ button_url=get_ticket_url(str(ticket.uuid)),
76
63
  )
77
64
 
78
65
  def send_support_reply_email(self, message):
@@ -89,7 +76,7 @@ class SupportEmailService:
89
76
  main_html_content=f'<div style="background: #f8f9fa; padding: 15px; border-left: 4px solid #007bff; margin: 15px 0;"><strong>Support Reply:</strong><br>{message.text}</div>',
90
77
  secondary_text="Please reply to continue the conversation. We're here to help!",
91
78
  button_text="View & Reply",
92
- button_url=self._get_ticket_url(ticket.uuid),
79
+ button_url=get_ticket_url(str(ticket.uuid)),
93
80
  )
94
81
 
95
82
  def send_ticket_status_changed_email(self, ticket, old_status, new_status):
@@ -109,7 +96,7 @@ class SupportEmailService:
109
96
  main_html_content=f'<div style="background: #f8f9fa; padding: 15px; border-left: 4px solid {color}; margin: 15px 0;"><strong>Status Update:</strong><br>From: <span style="color: #6c757d;">{old_status}</span><br>To: <span style="color: {color}; font-weight: bold;">{new_status}</span></div>',
110
97
  secondary_text="If you have any questions about this status change, please reply to your ticket.",
111
98
  button_text="View Ticket",
112
- button_url=self._get_ticket_url(ticket.uuid),
99
+ button_url=get_ticket_url(str(ticket.uuid)),
113
100
  )
114
101
 
115
102
  def send_ticket_resolved_email(self, ticket):
@@ -120,5 +107,5 @@ class SupportEmailService:
120
107
  main_html_content='<div style="background: #e8f5e8; padding: 15px; border-left: 4px solid #28a745; margin: 15px 0;"><strong>✅ Ticket Resolved</strong><br>Your issue has been successfully resolved by our support team.</div>',
121
108
  secondary_text="If you're satisfied with the resolution, no further action is needed. If you need additional help, feel free to reply to reopen the ticket.",
122
109
  button_text="View Resolution",
123
- button_url=self._get_ticket_url(ticket.uuid),
110
+ button_url=get_ticket_url(str(ticket.uuid)),
124
111
  )
@@ -12,8 +12,6 @@
12
12
 
13
13
  {% block extra_head %}
14
14
  {{ block.super }}
15
- <!-- Material Icons -->
16
- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
17
15
 
18
16
  <!-- Custom CSS -->
19
17
  <link rel="stylesheet" href="{% static 'tasks/css/dashboard.css' %}">
django_cfg/apps/urls.py CHANGED
@@ -50,8 +50,8 @@ def get_enabled_cfg_apps() -> List[str]:
50
50
  if base_module.is_payments_enabled():
51
51
  enabled_apps.append("django_cfg.apps.payments")
52
52
 
53
- if base_module.is_rpc_enabled():
54
- enabled_apps.append("django_cfg.apps.ipc")
53
+ if base_module.is_centrifugo_enabled():
54
+ enabled_apps.append("django_cfg.apps.centrifugo")
55
55
 
56
56
  return enabled_apps
57
57
 
@@ -166,9 +166,9 @@ APP_URL_MAP = {
166
166
  ("cfg/payments/", "django_cfg.apps.payments.urls"),
167
167
  # Payments v2.0: No separate urls_admin (uses Django Admin only)
168
168
  ],
169
- "django_cfg.apps.ipc": [
170
- ("cfg/ipc/", "django_cfg.apps.ipc.urls"),
171
- ("cfg/ipc/admin/", "django_cfg.apps.ipc.urls_admin"),
169
+ "django_cfg.apps.centrifugo": [
170
+ ("cfg/centrifugo/", "django_cfg.apps.centrifugo.urls"),
171
+ ("cfg/centrifugo/admin/", "django_cfg.apps.centrifugo.urls_admin"),
172
172
  ],
173
173
  }
174
174
 
@@ -15,7 +15,7 @@ from typing import Any, Dict, List, Optional
15
15
 
16
16
  from pydantic import BaseModel, Field, PrivateAttr, field_validator, model_validator
17
17
 
18
- from ...apps.ipc import DjangoCfgRPCConfig
18
+ from ...apps.centrifugo.services.client.config import DjangoCfgCentrifugoConfig
19
19
  from ...models import (
20
20
  ApiKeys,
21
21
  AxesConfig,
@@ -165,16 +165,6 @@ class DjangoConfig(BaseModel):
165
165
  description="Backend API URL",
166
166
  )
167
167
 
168
- ticket_url: str = Field(
169
- default="{site_url}/support/ticket/{uuid}",
170
- description="Support ticket URL template. Use {site_url} and {uuid} placeholders",
171
- )
172
-
173
- otp_url: str = Field(
174
- default="{site_url}/auth/otp/{code}",
175
- description="OTP verification URL template. Use {site_url} and {code} placeholders",
176
- )
177
-
178
168
  # === Core Django Settings ===
179
169
  secret_key: str = Field(
180
170
  ...,
@@ -316,10 +306,10 @@ class DjangoConfig(BaseModel):
316
306
  description="Background task processing configuration (Dramatiq)",
317
307
  )
318
308
 
319
- # === RPC Client Configuration ===
320
- django_ipc: Optional[DjangoCfgRPCConfig] = Field(
309
+ # === Centrifugo Configuration ===
310
+ centrifugo: Optional[DjangoCfgCentrifugoConfig] = Field(
321
311
  default=None,
322
- description="Django-CFG RPC Client configuration (WebSocket RPC communication)",
312
+ description="Centrifugo pub/sub configuration (WebSocket notifications with ACK tracking)",
323
313
  )
324
314
 
325
315
  # === API Configuration ===
@@ -568,36 +558,6 @@ class DjangoConfig(BaseModel):
568
558
 
569
559
  return self._django_settings
570
560
 
571
- def get_ticket_url(self, ticket_uuid: str) -> str:
572
- """
573
- Generate ticket URL using the configured template.
574
-
575
- Args:
576
- ticket_uuid: UUID of the support ticket
577
-
578
- Returns:
579
- Complete URL to the ticket
580
- """
581
- return self.ticket_url.format(
582
- site_url=self.site_url,
583
- uuid=ticket_uuid,
584
- )
585
-
586
- def get_otp_url(self, otp_code: str) -> str:
587
- """
588
- Generate OTP verification URL using the configured template.
589
-
590
- Args:
591
- otp_code: OTP verification code
592
-
593
- Returns:
594
- Complete URL to the OTP verification page
595
- """
596
- return self.otp_url.format(
597
- site_url=self.site_url,
598
- code=otp_code,
599
- )
600
-
601
561
  def invalidate_cache(self) -> None:
602
562
  """
603
563
  Invalidate cached Django settings.
@@ -133,8 +133,8 @@ class InstalledAppsBuilder:
133
133
  if self.config.payments and self.config.payments.enabled:
134
134
  apps.append("django_cfg.apps.payments")
135
135
 
136
- if self.config.django_ipc and self.config.django_ipc.enabled:
137
- apps.append("django_cfg.apps.ipc")
136
+ if self.config.centrifugo and self.config.centrifugo.enabled:
137
+ apps.append("django_cfg.apps.centrifugo")
138
138
 
139
139
  return apps
140
140
 
@@ -60,7 +60,7 @@ class ThirdPartyIntegrationsGenerator:
60
60
  settings.update(self._generate_telegram_settings())
61
61
  settings.update(self._generate_unfold_settings())
62
62
  settings.update(self._generate_constance_settings())
63
- settings.update(self._generate_rpc_settings())
63
+ settings.update(self._generate_centrifugo_settings())
64
64
 
65
65
  # Track enabled integrations
66
66
  if self.integrations:
@@ -116,20 +116,20 @@ class ThirdPartyIntegrationsGenerator:
116
116
 
117
117
  return constance_settings
118
118
 
119
- def _generate_rpc_settings(self) -> Dict[str, Any]:
119
+ def _generate_centrifugo_settings(self) -> Dict[str, Any]:
120
120
  """
121
- Generate RPC Client settings.
121
+ Generate Centrifugo settings.
122
122
 
123
123
  Returns:
124
- Dictionary with RPC configuration
124
+ Dictionary with Centrifugo configuration
125
125
  """
126
- if not hasattr(self.config, "django_ipc") or not self.config.django_ipc:
126
+ if not hasattr(self.config, "centrifugo") or not self.config.centrifugo:
127
127
  return {}
128
128
 
129
- rpc_settings = self.config.django_ipc.to_django_settings()
130
- self.integrations.append("django_ipc")
129
+ centrifugo_settings = self.config.centrifugo.to_django_settings()
130
+ self.integrations.append("centrifugo")
131
131
 
132
- return rpc_settings
132
+ return centrifugo_settings
133
133
 
134
134
  def get_enabled_integrations(self) -> List[str]:
135
135
  """
@@ -0,0 +1,5 @@
1
+ """Core utilities for django-cfg."""
2
+
3
+ from .url_helpers import get_otp_url, get_ticket_url
4
+
5
+ __all__ = ["get_ticket_url", "get_otp_url"]
@@ -0,0 +1,73 @@
1
+ """
2
+ URL generation utilities for django-cfg.
3
+
4
+ Provides helper functions to generate URLs dynamically from site_url configuration.
5
+ """
6
+
7
+ import logging
8
+ from typing import Optional
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ def get_ticket_url(ticket_uuid: str, fallback: str = "#ticket") -> str:
14
+ """
15
+ Generate support ticket URL on the fly from site_url.
16
+
17
+ Args:
18
+ ticket_uuid: UUID of the support ticket
19
+ fallback: Fallback URL if config is not available (default: "#ticket")
20
+
21
+ Returns:
22
+ Complete URL to the ticket
23
+
24
+ Example:
25
+ >>> get_ticket_url("abc-123-def")
26
+ "https://yoursite.com/support/ticket/abc-123-def"
27
+ """
28
+ try:
29
+ from django_cfg.core.state import get_current_config
30
+ config = get_current_config()
31
+
32
+ if config and hasattr(config, 'site_url'):
33
+ return f"{config.site_url}/support/ticket/{ticket_uuid}"
34
+ else:
35
+ logger.warning("Config or site_url not available for ticket URL generation")
36
+ return f"{fallback}-{ticket_uuid}"
37
+
38
+ except Exception as e:
39
+ logger.warning(f"Could not generate ticket URL: {e}")
40
+ return f"{fallback}-{ticket_uuid}"
41
+
42
+
43
+ def get_otp_url(otp_code: str, fallback: str = "#otp") -> str:
44
+ """
45
+ Generate OTP verification URL on the fly from site_url.
46
+
47
+ Args:
48
+ otp_code: OTP verification code
49
+ fallback: Fallback URL if config is not available (default: "#otp")
50
+
51
+ Returns:
52
+ Complete URL to the OTP verification page
53
+
54
+ Example:
55
+ >>> get_otp_url("123456")
56
+ "https://yoursite.com/auth/?otp=123456"
57
+ """
58
+ try:
59
+ from django_cfg.core.state import get_current_config
60
+ config = get_current_config()
61
+
62
+ if config and hasattr(config, 'site_url'):
63
+ return f"{config.site_url}/auth/?otp={otp_code}"
64
+ else:
65
+ logger.warning("Config or site_url not available for OTP URL generation")
66
+ return f"{fallback}-{otp_code}"
67
+
68
+ except Exception as e:
69
+ logger.warning(f"Could not generate OTP URL: {e}")
70
+ return f"{fallback}-{otp_code}"
71
+
72
+
73
+ __all__ = ["get_ticket_url", "get_otp_url"]
@@ -191,18 +191,18 @@ class BaseCfgModule(ABC):
191
191
 
192
192
  return False
193
193
 
194
- def is_rpc_enabled(self) -> bool:
194
+ def is_centrifugo_enabled(self) -> bool:
195
195
  """
196
- Check if django-cfg RPC Client is enabled.
196
+ Check if django-cfg Centrifugo is enabled.
197
197
 
198
198
  Returns:
199
- True if RPC Client is enabled, False otherwise
199
+ True if Centrifugo is enabled, False otherwise
200
200
  """
201
- rpc_config = self._get_config_key('django_ipc', None)
201
+ centrifugo_config = self._get_config_key('centrifugo', None)
202
202
 
203
- # Only handle DjangoCfgRPCConfig model
204
- if rpc_config and hasattr(rpc_config, 'enabled'):
205
- return rpc_config.enabled
203
+ # Check if centrifugo config exists and is enabled
204
+ if centrifugo_config and hasattr(centrifugo_config, 'enabled'):
205
+ return centrifugo_config.enabled
206
206
 
207
207
  return False
208
208
 
@@ -19,7 +19,7 @@ from .config import (
19
19
  )
20
20
 
21
21
  # Generators
22
- from .generator import PythonGenerator, TypeScriptGenerator
22
+ from .generator import GoGenerator, PythonGenerator, TypeScriptGenerator
23
23
 
24
24
  # Groups
25
25
  from .groups import GroupDetector, GroupManager
@@ -52,4 +52,5 @@ __all__ = [
52
52
  "OpenAPI31Parser",
53
53
  "PythonGenerator",
54
54
  "TypeScriptGenerator",
55
+ "GoGenerator",
55
56
  ]
@@ -188,6 +188,10 @@ class OpenAPIConfig(BaseModel):
188
188
  """Get TypeScript clients directory path."""
189
189
  return self.get_clients_dir() / "typescript"
190
190
 
191
+ def get_go_clients_dir(self) -> Path:
192
+ """Get Go clients directory path."""
193
+ return self.get_clients_dir() / "go"
194
+
191
195
  def get_archive_dir(self) -> Path:
192
196
  """Get archive directory path."""
193
197
  return self.get_output_path() / "archive"
@@ -204,6 +208,10 @@ class OpenAPIConfig(BaseModel):
204
208
  """Get TypeScript client directory for a group."""
205
209
  return self.get_typescript_clients_dir() / group_name
206
210
 
211
+ def get_group_go_dir(self, group_name: str) -> Path:
212
+ """Get Go client directory for a group."""
213
+ return self.get_go_clients_dir() / group_name
214
+
207
215
 
208
216
  __all__ = [
209
217
  "OpenAPIConfig",
@@ -24,15 +24,18 @@ from typing import Literal
24
24
 
25
25
  from ..ir import IRContext
26
26
  from .base import GeneratedFile
27
+ from .go import GoGenerator
27
28
  from .python import PythonGenerator
28
29
  from .typescript import TypeScriptGenerator
29
30
 
30
31
  __all__ = [
31
32
  "PythonGenerator",
32
33
  "TypeScriptGenerator",
34
+ "GoGenerator",
33
35
  "GeneratedFile",
34
36
  "generate_python",
35
37
  "generate_typescript",
38
+ "generate_go",
36
39
  "generate_client",
37
40
  ]
38
41
 
@@ -87,18 +90,52 @@ def generate_typescript(context: IRContext, output_dir: Path | None = None) -> l
87
90
  return files
88
91
 
89
92
 
93
+ def generate_go(context: IRContext, output_dir: Path | None = None, **kwargs) -> list[GeneratedFile]:
94
+ """
95
+ Generate Go client from IR.
96
+
97
+ Args:
98
+ context: IRContext from parser
99
+ output_dir: Optional output directory (saves files if provided)
100
+ **kwargs: Additional options (client_structure, package_config, etc.)
101
+
102
+ Returns:
103
+ List of GeneratedFile objects
104
+
105
+ Examples:
106
+ >>> files = generate_go(context)
107
+ >>> # Or save directly
108
+ >>> files = generate_go(context, output_dir=Path("./generated/go"))
109
+ >>> # With custom package config
110
+ >>> files = generate_go(
111
+ ... context,
112
+ ... package_config={"module_name": "github.com/user/api-client"},
113
+ ... generate_package_files=True
114
+ ... )
115
+ """
116
+ generator = GoGenerator(context, **kwargs)
117
+ files = generator.generate()
118
+
119
+ if output_dir:
120
+ generator.save_files(files, output_dir)
121
+
122
+ return files
123
+
124
+
90
125
  def generate_client(
91
126
  context: IRContext,
92
- language: Literal["python", "typescript"],
127
+ language: Literal["python", "typescript", "go"],
93
128
  output_dir: Path | None = None,
129
+ **kwargs,
94
130
  ) -> list[GeneratedFile]:
95
131
  """
96
132
  Generate client for specified language.
97
133
 
98
134
  Args:
99
135
  context: IRContext from parser
100
- language: Target language ('python' or 'typescript')
136
+ language: Target language ('python', 'typescript', or 'go')
101
137
  output_dir: Optional output directory
138
+ **kwargs: Additional language-specific options
102
139
 
103
140
  Returns:
104
141
  List of GeneratedFile objects
@@ -106,10 +143,13 @@ def generate_client(
106
143
  Examples:
107
144
  >>> files = generate_client(context, "python")
108
145
  >>> files = generate_client(context, "typescript", Path("./generated"))
146
+ >>> files = generate_client(context, "go", Path("./generated"), generate_package_files=True)
109
147
  """
110
148
  if language == "python":
111
149
  return generate_python(context, output_dir)
112
150
  elif language == "typescript":
113
151
  return generate_typescript(context, output_dir)
152
+ elif language == "go":
153
+ return generate_go(context, output_dir, **kwargs)
114
154
  else:
115
155
  raise ValueError(f"Unsupported language: {language}")
@@ -0,0 +1,14 @@
1
+ """
2
+ Go Generator - Generates Go client (net/http).
3
+
4
+ This generator creates a complete Go API client from IR:
5
+ - Go structs (Request/Response/Patch splits)
6
+ - Enum types with constants
7
+ - net/http client for HTTP requests
8
+ - Type-safe operations
9
+ - Context-aware requests
10
+ """
11
+
12
+ from .generator import GoGenerator
13
+
14
+ __all__ = ['GoGenerator']
@@ -0,0 +1,124 @@
1
+ """
2
+ Client Generator - Generates Go HTTP client with operations.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from datetime import datetime
8
+ from typing import TYPE_CHECKING
9
+
10
+ from .naming import to_pascal_case
11
+
12
+ if TYPE_CHECKING:
13
+ from jinja2 import Environment
14
+
15
+ from ...ir import IRContext
16
+ from ..base import GeneratedFile
17
+ from .generator import GoGenerator
18
+ from .operations_generator import OperationsGenerator
19
+
20
+
21
+ class ClientGenerator:
22
+ """Generates Go HTTP client code."""
23
+
24
+ def __init__(
25
+ self,
26
+ jinja_env: Environment,
27
+ context: IRContext,
28
+ generator: GoGenerator,
29
+ operations_gen: OperationsGenerator,
30
+ ):
31
+ """Initialize client generator."""
32
+ self.jinja_env = jinja_env
33
+ self.context = context
34
+ self.generator = generator
35
+ self.operations_gen = operations_gen
36
+
37
+ def generate_client_file(self) -> GeneratedFile:
38
+ """Generate client.go with HTTP client and all operations."""
39
+ template = self.jinja_env.get_template("client.go.j2")
40
+
41
+ # Generate all operation methods
42
+ operations = []
43
+ for op_id, operation in sorted(self.context.operations.items()):
44
+ op_method = self.operations_gen.generate_operation_method(operation)
45
+ operations.append(op_method)
46
+
47
+ content = template.render(
48
+ package_name=self.generator.package_name,
49
+ operations=operations,
50
+ generated_at=datetime.now().isoformat(),
51
+ )
52
+
53
+ return self.generator._create_generated_file(
54
+ path="client.go",
55
+ content=content,
56
+ description="HTTP API client"
57
+ )
58
+
59
+ def generate_main_client_file(self, ops_by_tag: dict) -> GeneratedFile:
60
+ """Generate main client.go for namespaced structure."""
61
+ template = self.jinja_env.get_template("main_client.go.j2")
62
+
63
+ subclients = []
64
+ for tag in sorted(ops_by_tag.keys()):
65
+ folder_name = self.generator.tag_and_app_to_folder_name(tag, ops_by_tag[tag])
66
+ subclients.append({
67
+ "name": to_pascal_case(folder_name),
68
+ "package": folder_name,
69
+ })
70
+
71
+ content = template.render(
72
+ package_name=self.generator.package_name,
73
+ module_name=self.generator.package_config.get("module_name", "apiclient"),
74
+ subclients=subclients,
75
+ generated_at=datetime.now().isoformat(),
76
+ )
77
+
78
+ return self.generator._create_generated_file(
79
+ path="client.go",
80
+ content=content,
81
+ description="Main API client"
82
+ )
83
+
84
+ def generate_subpackage_client_file(self, tag: str, operations: list) -> GeneratedFile:
85
+ """Generate client.go for a specific subpackage with its operations."""
86
+ template = self.jinja_env.get_template("operations_client.go.j2")
87
+
88
+ # Get folder name for this tag
89
+ folder_name = self.generator.tag_and_app_to_folder_name(tag, operations)
90
+
91
+ # Generate operation methods for this tag
92
+ operation_methods = []
93
+ has_request_body = False
94
+ has_query_params = False
95
+ has_path_params = False
96
+
97
+ for operation in sorted(operations, key=lambda op: op.operation_id):
98
+ op_method = self.operations_gen.generate_operation_method(operation, remove_tag_prefix=True)
99
+ operation_methods.append(op_method)
100
+
101
+ # Check what imports we need
102
+ if op_method.get("request_type"):
103
+ has_request_body = True
104
+ if any(p.get("location") == "query" for p in op_method.get("parameters", [])):
105
+ has_query_params = True
106
+ if any(p.get("location") == "path" for p in op_method.get("parameters", [])):
107
+ has_path_params = True
108
+
109
+ content = template.render(
110
+ package_name=folder_name,
111
+ module_name=self.generator.package_config.get("module_name", "apiclient"),
112
+ parent_package=self.generator.package_name,
113
+ operations=operation_methods,
114
+ has_request_body=has_request_body,
115
+ has_query_params=has_query_params,
116
+ has_path_params=has_path_params,
117
+ generated_at=datetime.now().isoformat(),
118
+ )
119
+
120
+ return self.generator._create_generated_file(
121
+ path=f"{folder_name}/client.go",
122
+ content=content,
123
+ description=f"HTTP client for {tag}"
124
+ )