django-cfg 1.4.62__py3-none-any.whl → 1.4.64__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 (181) 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 +582 -0
  62. django_cfg/apps/centrifugo/services/client/config.py +236 -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 +380 -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.62.dist-info → django_cfg-1.4.64.dist-info}/METADATA +1 -1
  139. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/RECORD +142 -70
  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 -232
  145. django_cfg/apps/ipc/apps.py +0 -98
  146. django_cfg/apps/ipc/migrations/0001_initial.py +0 -137
  147. django_cfg/apps/ipc/migrations/0002_rpclog_is_event.py +0 -23
  148. django_cfg/apps/ipc/migrations/__init__.py +0 -0
  149. django_cfg/apps/ipc/models.py +0 -229
  150. django_cfg/apps/ipc/serializers/__init__.py +0 -29
  151. django_cfg/apps/ipc/serializers/serializers.py +0 -343
  152. django_cfg/apps/ipc/services/__init__.py +0 -7
  153. django_cfg/apps/ipc/services/client/__init__.py +0 -23
  154. django_cfg/apps/ipc/services/client/client.py +0 -621
  155. django_cfg/apps/ipc/services/client/config.py +0 -214
  156. django_cfg/apps/ipc/services/client/exceptions.py +0 -201
  157. django_cfg/apps/ipc/services/logging.py +0 -239
  158. django_cfg/apps/ipc/services/monitor.py +0 -466
  159. django_cfg/apps/ipc/services/rpc_log_consumer.py +0 -330
  160. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/main.mjs +0 -269
  161. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/overview.mjs +0 -259
  162. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/testing.mjs +0 -375
  163. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard.mjs.old +0 -441
  164. django_cfg/apps/ipc/templates/django_cfg_ipc/components/methods_content.html +0 -22
  165. django_cfg/apps/ipc/templates/django_cfg_ipc/components/notifications_content.html +0 -9
  166. django_cfg/apps/ipc/templates/django_cfg_ipc/components/overview_content.html +0 -9
  167. django_cfg/apps/ipc/templates/django_cfg_ipc/components/requests_content.html +0 -23
  168. django_cfg/apps/ipc/templates/django_cfg_ipc/components/system_status.html +0 -47
  169. django_cfg/apps/ipc/templates/django_cfg_ipc/components/testing_tools.html +0 -184
  170. django_cfg/apps/ipc/templates/django_cfg_ipc/layout/base.html +0 -71
  171. django_cfg/apps/ipc/templates/django_cfg_ipc/pages/dashboard.html +0 -56
  172. django_cfg/apps/ipc/urls.py +0 -23
  173. django_cfg/apps/ipc/views/__init__.py +0 -13
  174. django_cfg/apps/ipc/views/dashboard.py +0 -15
  175. django_cfg/apps/ipc/views/monitoring.py +0 -251
  176. django_cfg/apps/ipc/views/testing.py +0 -285
  177. django_cfg/static/js/api/ipc/client.mjs +0 -114
  178. django_cfg/static/js/api/ipc/index.mjs +0 -13
  179. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/WHEEL +0 -0
  180. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/entry_points.txt +0 -0
  181. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,11 @@
1
+ """
2
+ Centrifugo Models.
3
+
4
+ Django models for tracking Centrifugo publish operations.
5
+ """
6
+
7
+ from .centrifugo_log import CentrifugoLog
8
+
9
+ __all__ = [
10
+ "CentrifugoLog",
11
+ ]
@@ -0,0 +1,210 @@
1
+ """
2
+ Centrifugo Log Model.
3
+
4
+ Django model for tracking Centrifugo publish operations.
5
+ Mirrors legacy WebSocket solution RPCLog patterns for easy migration.
6
+ """
7
+
8
+ from django.conf import settings
9
+ from django.db import models
10
+ from django.utils import timezone
11
+
12
+
13
+ class CentrifugoLog(models.Model):
14
+ """
15
+ Log of Centrifugo publish operations.
16
+
17
+ Tracks all publish calls with ACK tracking, mirroring RPCLog functionality.
18
+ Provides observability for debugging and monitoring.
19
+
20
+ Fields mirror RPCLog for migration compatibility:
21
+ - correlation_id → message_id
22
+ - method → channel
23
+ - params → data
24
+ - status → status
25
+ - duration_ms → duration_ms
26
+ - is_event → is_notification
27
+
28
+ Example:
29
+ >>> log = CentrifugoLog.objects.create(
30
+ ... message_id="abc123",
31
+ ... channel="user#456",
32
+ ... data={"title": "Hello", "message": "World"},
33
+ ... wait_for_ack=True
34
+ ... )
35
+ >>> log.mark_success(acks_received=1, duration_ms=125)
36
+ """
37
+
38
+ # Custom manager
39
+ from ..managers.centrifugo_log import CentrifugoLogManager
40
+
41
+ objects: CentrifugoLogManager = CentrifugoLogManager()
42
+
43
+ class StatusChoices(models.TextChoices):
44
+ """Status of publish operation."""
45
+
46
+ PENDING = "pending", "Pending"
47
+ SUCCESS = "success", "Success"
48
+ FAILED = "failed", "Failed"
49
+ TIMEOUT = "timeout", "Timeout"
50
+ PARTIAL = "partial", "Partial Delivery" # Some ACKs received, not all
51
+
52
+ # Identity
53
+ message_id = models.CharField(
54
+ max_length=100,
55
+ unique=True,
56
+ db_index=True,
57
+ help_text="Unique message identifier (UUID)",
58
+ )
59
+
60
+ # Publish details
61
+ channel = models.CharField(
62
+ max_length=200,
63
+ db_index=True,
64
+ help_text="Centrifugo channel (e.g., user#123, broadcast)",
65
+ )
66
+
67
+ data = models.JSONField(
68
+ help_text="Published data (JSON payload)",
69
+ )
70
+
71
+ # ACK tracking
72
+ wait_for_ack = models.BooleanField(
73
+ default=False,
74
+ db_index=True,
75
+ help_text="Whether this publish waited for ACK",
76
+ )
77
+
78
+ ack_timeout = models.IntegerField(
79
+ null=True,
80
+ blank=True,
81
+ help_text="ACK timeout in seconds",
82
+ )
83
+
84
+ acks_received = models.IntegerField(
85
+ default=0,
86
+ help_text="Number of ACKs received",
87
+ )
88
+
89
+ acks_expected = models.IntegerField(
90
+ null=True,
91
+ blank=True,
92
+ help_text="Number of ACKs expected (if known)",
93
+ )
94
+
95
+ # Status tracking
96
+ status = models.CharField(
97
+ max_length=20,
98
+ choices=StatusChoices.choices,
99
+ default=StatusChoices.PENDING,
100
+ db_index=True,
101
+ help_text="Current status of publish operation",
102
+ )
103
+
104
+ error_code = models.CharField(
105
+ max_length=100,
106
+ null=True,
107
+ blank=True,
108
+ help_text="Error code if failed",
109
+ )
110
+
111
+ error_message = models.TextField(
112
+ null=True,
113
+ blank=True,
114
+ help_text="Error message if failed",
115
+ )
116
+
117
+ # Performance
118
+ duration_ms = models.IntegerField(
119
+ null=True,
120
+ blank=True,
121
+ help_text="Total duration in milliseconds",
122
+ )
123
+
124
+ # Metadata
125
+ is_notification = models.BooleanField(
126
+ default=True,
127
+ db_index=True,
128
+ help_text="Whether this is a notification (vs other pub type)",
129
+ )
130
+
131
+ user = models.ForeignKey(
132
+ settings.AUTH_USER_MODEL,
133
+ on_delete=models.SET_NULL,
134
+ null=True,
135
+ blank=True,
136
+ related_name="centrifugo_logs",
137
+ help_text="User who triggered the publish (if applicable)",
138
+ )
139
+
140
+ caller_ip = models.GenericIPAddressField(
141
+ null=True,
142
+ blank=True,
143
+ help_text="IP address of caller",
144
+ )
145
+
146
+ user_agent = models.TextField(
147
+ null=True,
148
+ blank=True,
149
+ help_text="User agent of caller",
150
+ )
151
+
152
+ # Timestamps
153
+ created_at = models.DateTimeField(
154
+ auto_now_add=True,
155
+ db_index=True,
156
+ help_text="When publish was initiated",
157
+ )
158
+
159
+ completed_at = models.DateTimeField(
160
+ null=True,
161
+ blank=True,
162
+ db_index=True,
163
+ help_text="When publish completed (success/failure/timeout)",
164
+ )
165
+
166
+ class Meta:
167
+ db_table = "django_cfg_centrifugo_log"
168
+ ordering = ["-created_at"]
169
+ indexes = [
170
+ models.Index(fields=["channel", "-created_at"]),
171
+ models.Index(fields=["status", "-created_at"]),
172
+ models.Index(fields=["wait_for_ack", "status"]),
173
+ models.Index(fields=["user", "-created_at"]),
174
+ ]
175
+ verbose_name = "Centrifugo Log"
176
+ verbose_name_plural = "Centrifugo Logs"
177
+
178
+ def __str__(self) -> str:
179
+ """String representation."""
180
+ return f"{self.channel} ({self.message_id[:8]}...) - {self.status}"
181
+
182
+ @property
183
+ def is_completed(self) -> bool:
184
+ """Check if publish is completed (any terminal status)."""
185
+ return self.status in [
186
+ self.StatusChoices.SUCCESS,
187
+ self.StatusChoices.FAILED,
188
+ self.StatusChoices.TIMEOUT,
189
+ self.StatusChoices.PARTIAL,
190
+ ]
191
+
192
+ @property
193
+ def is_successful(self) -> bool:
194
+ """Check if publish was successful."""
195
+ return self.status == self.StatusChoices.SUCCESS
196
+
197
+ @property
198
+ def delivery_rate(self) -> float | None:
199
+ """
200
+ Calculate delivery rate (ACKs received / expected).
201
+
202
+ Returns:
203
+ Delivery rate (0.0 to 1.0) or None if unknown
204
+ """
205
+ if self.acks_expected and self.acks_expected > 0:
206
+ return self.acks_received / self.acks_expected
207
+ return None
208
+
209
+
210
+ __all__ = ["CentrifugoLog"]
@@ -0,0 +1,106 @@
1
+ """
2
+ Global registry for RPC handlers.
3
+
4
+ Stores metadata about registered handlers for code generation.
5
+ """
6
+
7
+ import logging
8
+ from typing import Dict, List, Any, Callable, Optional, Type
9
+ from pydantic import BaseModel
10
+ from dataclasses import dataclass
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ @dataclass
16
+ class RegisteredHandler:
17
+ """
18
+ Metadata about registered RPC handler.
19
+
20
+ Attributes:
21
+ name: RPC method name (e.g., "tasks.get_stats")
22
+ handler: Handler function
23
+ param_type: Pydantic model for parameters
24
+ return_type: Pydantic model for return value
25
+ docstring: Handler documentation
26
+ """
27
+ name: str
28
+ handler: Callable
29
+ param_type: Optional[Type[BaseModel]]
30
+ return_type: Optional[Type[BaseModel]]
31
+ docstring: Optional[str]
32
+
33
+
34
+ class RPCRegistry:
35
+ """
36
+ Global registry for RPC handlers.
37
+
38
+ Used by @websocket_rpc decorator to register handlers
39
+ and by codegen to discover available methods.
40
+ """
41
+
42
+ def __init__(self):
43
+ self._handlers: Dict[str, RegisteredHandler] = {}
44
+
45
+ def register(
46
+ self,
47
+ name: str,
48
+ handler: Callable,
49
+ param_type: Optional[Type[BaseModel]] = None,
50
+ return_type: Optional[Type[BaseModel]] = None,
51
+ docstring: Optional[str] = None,
52
+ ) -> None:
53
+ """
54
+ Register RPC handler.
55
+
56
+ Args:
57
+ name: RPC method name (e.g., "tasks.get_stats")
58
+ handler: Handler function
59
+ param_type: Pydantic model for parameters
60
+ return_type: Pydantic model for return value
61
+ docstring: Handler documentation
62
+ """
63
+ if name in self._handlers:
64
+ logger.warning(f"Handler '{name}' already registered, overwriting")
65
+
66
+ self._handlers[name] = RegisteredHandler(
67
+ name=name,
68
+ handler=handler,
69
+ param_type=param_type,
70
+ return_type=return_type,
71
+ docstring=docstring,
72
+ )
73
+
74
+ logger.debug(f"Registered RPC handler: {name}")
75
+
76
+ def get_handler(self, name: str) -> Optional[RegisteredHandler]:
77
+ """Get handler by name."""
78
+ return self._handlers.get(name)
79
+
80
+ def get_all_handlers(self) -> List[RegisteredHandler]:
81
+ """Get all registered handlers."""
82
+ return list(self._handlers.values())
83
+
84
+ def list_methods(self) -> List[str]:
85
+ """List all registered method names."""
86
+ return list(self._handlers.keys())
87
+
88
+ def clear(self) -> None:
89
+ """Clear all registered handlers (for testing)."""
90
+ self._handlers.clear()
91
+
92
+
93
+ # Global registry instance
94
+ _global_registry = RPCRegistry()
95
+
96
+
97
+ def get_global_registry() -> RPCRegistry:
98
+ """Get global RPC registry instance."""
99
+ return _global_registry
100
+
101
+
102
+ __all__ = [
103
+ "RegisteredHandler",
104
+ "RPCRegistry",
105
+ "get_global_registry",
106
+ ]
@@ -0,0 +1,125 @@
1
+ """
2
+ MessageRouter for Centrifugo RPC handlers.
3
+
4
+ Compatible with legacy WebSocket solution's MessageRouter interface for code generation.
5
+ """
6
+
7
+ import logging
8
+ from typing import Dict, Callable, Any
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class MessageRouter:
14
+ """
15
+ Message router for Centrifugo RPC handlers.
16
+
17
+ Provides legacy WebSocket solution compatible interface for handler registration
18
+ and discovery by codegen system.
19
+ """
20
+
21
+ def __init__(self):
22
+ """Initialize message router."""
23
+ self._handlers: Dict[str, Callable] = {}
24
+
25
+ def register(self, method_name: str):
26
+ """
27
+ Decorator to register RPC handler.
28
+
29
+ Args:
30
+ method_name: RPC method name (e.g., "tasks.get_stats")
31
+
32
+ Returns:
33
+ Decorator function
34
+
35
+ Example:
36
+ >>> router = MessageRouter()
37
+ >>>
38
+ >>> @router.register("tasks.get_stats")
39
+ >>> async def handle_get_stats(conn, params: TaskStatsParams) -> TaskStatsResult:
40
+ ... return TaskStatsResult(total=100, completed=50)
41
+ """
42
+ def decorator(handler_func: Callable) -> Callable:
43
+ if method_name in self._handlers:
44
+ logger.warning(f"Handler '{method_name}' already registered, overwriting")
45
+
46
+ self._handlers[method_name] = handler_func
47
+ logger.debug(f"Registered handler: {method_name}")
48
+
49
+ return handler_func
50
+
51
+ return decorator
52
+
53
+ def get_handler(self, method_name: str) -> Callable:
54
+ """
55
+ Get handler by method name.
56
+
57
+ Args:
58
+ method_name: RPC method name
59
+
60
+ Returns:
61
+ Handler function
62
+
63
+ Raises:
64
+ KeyError: If handler not found
65
+ """
66
+ return self._handlers[method_name]
67
+
68
+ def has_handler(self, method_name: str) -> bool:
69
+ """
70
+ Check if handler exists.
71
+
72
+ Args:
73
+ method_name: RPC method name
74
+
75
+ Returns:
76
+ True if handler registered
77
+ """
78
+ return method_name in self._handlers
79
+
80
+ def list_methods(self) -> list[str]:
81
+ """
82
+ List all registered method names.
83
+
84
+ Returns:
85
+ List of method names
86
+ """
87
+ return list(self._handlers.keys())
88
+
89
+ async def handle_message(self, method_name: str, params: Any, conn: Any = None) -> Any:
90
+ """
91
+ Handle RPC message.
92
+
93
+ Args:
94
+ method_name: RPC method name
95
+ params: Method parameters
96
+ conn: Connection object (optional)
97
+
98
+ Returns:
99
+ Handler result
100
+
101
+ Raises:
102
+ KeyError: If handler not found
103
+ """
104
+ handler = self.get_handler(method_name)
105
+
106
+ # Call handler with or without connection
107
+ if conn is not None:
108
+ return await handler(conn, params)
109
+ else:
110
+ return await handler(params)
111
+
112
+
113
+ # Global router instance
114
+ _global_router = MessageRouter()
115
+
116
+
117
+ def get_global_router() -> MessageRouter:
118
+ """Get global MessageRouter instance."""
119
+ return _global_router
120
+
121
+
122
+ __all__ = [
123
+ "MessageRouter",
124
+ "get_global_router",
125
+ ]
@@ -0,0 +1,40 @@
1
+ """
2
+ Serializers for Centrifugo module.
3
+ """
4
+
5
+ from .admin_api import (
6
+ CentrifugoChannelsRequest,
7
+ CentrifugoChannelsResponse,
8
+ CentrifugoHistoryRequest,
9
+ CentrifugoHistoryResponse,
10
+ CentrifugoInfoRequest,
11
+ CentrifugoInfoResponse,
12
+ CentrifugoPresenceRequest,
13
+ CentrifugoPresenceResponse,
14
+ CentrifugoPresenceStatsRequest,
15
+ CentrifugoPresenceStatsResponse,
16
+ )
17
+ from .channels import ChannelListSerializer, ChannelStatsSerializer
18
+ from .health import HealthCheckSerializer
19
+ from .publishes import RecentPublishesSerializer
20
+ from .stats import OverviewStatsSerializer
21
+
22
+ __all__ = [
23
+ # Monitoring API (Django logs)
24
+ "HealthCheckSerializer",
25
+ "OverviewStatsSerializer",
26
+ "RecentPublishesSerializer",
27
+ "ChannelStatsSerializer",
28
+ "ChannelListSerializer",
29
+ # Admin API (Centrifugo server)
30
+ "CentrifugoInfoRequest",
31
+ "CentrifugoInfoResponse",
32
+ "CentrifugoChannelsRequest",
33
+ "CentrifugoChannelsResponse",
34
+ "CentrifugoPresenceRequest",
35
+ "CentrifugoPresenceResponse",
36
+ "CentrifugoPresenceStatsRequest",
37
+ "CentrifugoPresenceStatsResponse",
38
+ "CentrifugoHistoryRequest",
39
+ "CentrifugoHistoryResponse",
40
+ ]