django-cfg 1.5.8__py3-none-any.whl → 1.5.20__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 (159) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/api/commands/serializers.py +152 -0
  3. django_cfg/apps/api/commands/views.py +32 -0
  4. django_cfg/apps/business/accounts/management/commands/otp_test.py +5 -2
  5. django_cfg/apps/business/accounts/serializers/profile.py +42 -0
  6. django_cfg/apps/business/agents/management/commands/create_agent.py +5 -194
  7. django_cfg/apps/business/agents/management/commands/load_agent_templates.py +205 -0
  8. django_cfg/apps/business/agents/management/commands/orchestrator_status.py +4 -2
  9. django_cfg/apps/business/knowbase/management/commands/knowbase_stats.py +4 -2
  10. django_cfg/apps/business/knowbase/management/commands/setup_knowbase.py +4 -2
  11. django_cfg/apps/business/newsletter/management/commands/test_newsletter.py +5 -2
  12. django_cfg/apps/business/payments/management/commands/check_payment_status.py +4 -2
  13. django_cfg/apps/business/payments/management/commands/create_payment.py +4 -2
  14. django_cfg/apps/business/payments/management/commands/sync_currencies.py +4 -2
  15. django_cfg/apps/business/support/serializers.py +3 -2
  16. django_cfg/apps/integrations/centrifugo/apps.py +2 -1
  17. django_cfg/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +151 -12
  18. django_cfg/apps/integrations/centrifugo/management/commands/generate_centrifugo_clients.py +6 -6
  19. django_cfg/apps/integrations/centrifugo/serializers/__init__.py +2 -1
  20. django_cfg/apps/integrations/centrifugo/serializers/publishes.py +22 -2
  21. django_cfg/apps/integrations/centrifugo/services/__init__.py +6 -0
  22. django_cfg/apps/integrations/centrifugo/services/client/__init__.py +6 -1
  23. django_cfg/apps/integrations/centrifugo/services/client/direct_client.py +282 -0
  24. django_cfg/apps/integrations/centrifugo/services/publisher.py +371 -0
  25. django_cfg/apps/integrations/centrifugo/services/token_generator.py +122 -0
  26. django_cfg/apps/integrations/centrifugo/urls.py +8 -0
  27. django_cfg/apps/integrations/centrifugo/views/__init__.py +2 -0
  28. django_cfg/apps/integrations/centrifugo/views/monitoring.py +25 -40
  29. django_cfg/apps/integrations/centrifugo/views/testing_api.py +0 -79
  30. django_cfg/apps/integrations/centrifugo/views/token_api.py +101 -0
  31. django_cfg/apps/integrations/centrifugo/views/wrapper.py +257 -0
  32. django_cfg/apps/integrations/grpc/admin/__init__.py +7 -1
  33. django_cfg/apps/integrations/grpc/admin/config.py +113 -9
  34. django_cfg/apps/integrations/grpc/admin/grpc_api_key.py +129 -0
  35. django_cfg/apps/integrations/grpc/admin/grpc_request_log.py +72 -63
  36. django_cfg/apps/integrations/grpc/admin/grpc_server_status.py +236 -0
  37. django_cfg/apps/integrations/grpc/auth/__init__.py +11 -3
  38. django_cfg/apps/integrations/grpc/auth/api_key_auth.py +320 -0
  39. django_cfg/apps/integrations/grpc/centrifugo/__init__.py +29 -0
  40. django_cfg/apps/integrations/grpc/centrifugo/bridge.py +277 -0
  41. django_cfg/apps/integrations/grpc/centrifugo/config.py +167 -0
  42. django_cfg/apps/integrations/grpc/centrifugo/demo.py +626 -0
  43. django_cfg/apps/integrations/grpc/centrifugo/test_publish.py +229 -0
  44. django_cfg/apps/integrations/grpc/centrifugo/transformers.py +89 -0
  45. django_cfg/apps/integrations/grpc/interceptors/__init__.py +3 -1
  46. django_cfg/apps/integrations/grpc/interceptors/centrifugo.py +541 -0
  47. django_cfg/apps/integrations/grpc/interceptors/logging.py +17 -20
  48. django_cfg/apps/integrations/grpc/interceptors/metrics.py +15 -14
  49. django_cfg/apps/integrations/grpc/interceptors/request_logger.py +79 -59
  50. django_cfg/apps/integrations/grpc/management/commands/compile_proto.py +105 -0
  51. django_cfg/apps/integrations/grpc/management/commands/generate_protos.py +185 -0
  52. django_cfg/apps/integrations/grpc/management/commands/rungrpc.py +474 -95
  53. django_cfg/apps/integrations/grpc/management/commands/test_grpc_integration.py +75 -0
  54. django_cfg/apps/integrations/grpc/management/proto/__init__.py +3 -0
  55. django_cfg/apps/integrations/grpc/management/proto/compiler.py +194 -0
  56. django_cfg/apps/integrations/grpc/managers/__init__.py +2 -0
  57. django_cfg/apps/integrations/grpc/managers/grpc_api_key.py +192 -0
  58. django_cfg/apps/integrations/grpc/managers/grpc_server_status.py +19 -11
  59. django_cfg/apps/integrations/grpc/migrations/0005_grpcapikey.py +143 -0
  60. django_cfg/apps/integrations/grpc/migrations/0006_grpcrequestlog_api_key_and_more.py +34 -0
  61. django_cfg/apps/integrations/grpc/models/__init__.py +2 -0
  62. django_cfg/apps/integrations/grpc/models/grpc_api_key.py +198 -0
  63. django_cfg/apps/integrations/grpc/models/grpc_request_log.py +11 -0
  64. django_cfg/apps/integrations/grpc/models/grpc_server_status.py +39 -4
  65. django_cfg/apps/integrations/grpc/serializers/__init__.py +22 -6
  66. django_cfg/apps/integrations/grpc/serializers/api_keys.py +63 -0
  67. django_cfg/apps/integrations/grpc/serializers/charts.py +118 -120
  68. django_cfg/apps/integrations/grpc/serializers/config.py +65 -51
  69. django_cfg/apps/integrations/grpc/serializers/health.py +7 -7
  70. django_cfg/apps/integrations/grpc/serializers/proto_files.py +74 -0
  71. django_cfg/apps/integrations/grpc/serializers/requests.py +13 -7
  72. django_cfg/apps/integrations/grpc/serializers/service_registry.py +181 -112
  73. django_cfg/apps/integrations/grpc/serializers/services.py +14 -32
  74. django_cfg/apps/integrations/grpc/serializers/stats.py +50 -12
  75. django_cfg/apps/integrations/grpc/serializers/testing.py +66 -58
  76. django_cfg/apps/integrations/grpc/services/__init__.py +2 -0
  77. django_cfg/apps/integrations/grpc/services/discovery.py +7 -1
  78. django_cfg/apps/integrations/grpc/services/monitoring_service.py +149 -43
  79. django_cfg/apps/integrations/grpc/services/proto_files_manager.py +268 -0
  80. django_cfg/apps/integrations/grpc/services/service_registry.py +48 -46
  81. django_cfg/apps/integrations/grpc/services/testing_service.py +10 -15
  82. django_cfg/apps/integrations/grpc/urls.py +8 -0
  83. django_cfg/apps/integrations/grpc/utils/SERVER_LOGGING.md +164 -0
  84. django_cfg/apps/integrations/grpc/utils/__init__.py +4 -13
  85. django_cfg/apps/integrations/grpc/utils/integration_test.py +334 -0
  86. django_cfg/apps/integrations/grpc/utils/proto_gen.py +48 -8
  87. django_cfg/apps/integrations/grpc/utils/streaming_logger.py +378 -0
  88. django_cfg/apps/integrations/grpc/views/__init__.py +4 -0
  89. django_cfg/apps/integrations/grpc/views/api_keys.py +255 -0
  90. django_cfg/apps/integrations/grpc/views/charts.py +21 -14
  91. django_cfg/apps/integrations/grpc/views/config.py +8 -6
  92. django_cfg/apps/integrations/grpc/views/monitoring.py +51 -79
  93. django_cfg/apps/integrations/grpc/views/proto_files.py +214 -0
  94. django_cfg/apps/integrations/grpc/views/services.py +30 -21
  95. django_cfg/apps/integrations/grpc/views/testing.py +45 -43
  96. django_cfg/apps/integrations/rq/views/jobs.py +19 -9
  97. django_cfg/apps/integrations/rq/views/schedule.py +7 -3
  98. django_cfg/apps/system/dashboard/serializers/commands.py +25 -1
  99. django_cfg/apps/system/dashboard/serializers/config.py +95 -9
  100. django_cfg/apps/system/dashboard/serializers/statistics.py +9 -4
  101. django_cfg/apps/system/dashboard/services/commands_service.py +12 -1
  102. django_cfg/apps/system/frontend/views.py +87 -6
  103. django_cfg/apps/system/maintenance/management/commands/maintenance.py +5 -2
  104. django_cfg/apps/system/maintenance/management/commands/process_scheduled_maintenance.py +4 -2
  105. django_cfg/apps/system/maintenance/management/commands/sync_cloudflare.py +5 -2
  106. django_cfg/config.py +33 -0
  107. django_cfg/core/builders/security_builder.py +1 -0
  108. django_cfg/core/generation/integration_generators/api.py +2 -0
  109. django_cfg/core/generation/integration_generators/grpc_generator.py +30 -32
  110. django_cfg/management/commands/check_endpoints.py +2 -2
  111. django_cfg/management/commands/check_settings.py +3 -10
  112. django_cfg/management/commands/clear_constance.py +3 -10
  113. django_cfg/management/commands/create_token.py +4 -11
  114. django_cfg/management/commands/list_urls.py +4 -10
  115. django_cfg/management/commands/migrate_all.py +18 -12
  116. django_cfg/management/commands/migrator.py +4 -11
  117. django_cfg/management/commands/script.py +4 -10
  118. django_cfg/management/commands/show_config.py +8 -16
  119. django_cfg/management/commands/show_urls.py +5 -11
  120. django_cfg/management/commands/superuser.py +4 -11
  121. django_cfg/management/commands/tree.py +5 -10
  122. django_cfg/management/utils/README.md +402 -0
  123. django_cfg/management/utils/__init__.py +29 -0
  124. django_cfg/management/utils/mixins.py +176 -0
  125. django_cfg/middleware/pagination.py +53 -54
  126. django_cfg/models/api/grpc/__init__.py +15 -21
  127. django_cfg/models/api/grpc/config.py +155 -73
  128. django_cfg/models/ngrok/config.py +7 -6
  129. django_cfg/modules/django_client/core/generator/python/files_generator.py +5 -13
  130. django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +16 -4
  131. django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +2 -3
  132. django_cfg/modules/django_client/core/generator/typescript/files_generator.py +6 -5
  133. django_cfg/modules/django_client/core/generator/typescript/generator.py +26 -0
  134. django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +7 -1
  135. django_cfg/modules/django_client/core/generator/typescript/models_generator.py +5 -0
  136. django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +11 -0
  137. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +1 -0
  138. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +29 -1
  139. django_cfg/modules/django_client/core/generator/typescript/templates/hooks/hooks.ts.jinja +4 -0
  140. django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +12 -8
  141. django_cfg/modules/django_client/core/ir/schema.py +15 -1
  142. django_cfg/modules/django_client/core/parser/base.py +126 -30
  143. django_cfg/modules/django_client/management/commands/generate_client.py +5 -2
  144. django_cfg/modules/django_client/management/commands/validate_openapi.py +5 -2
  145. django_cfg/modules/django_email/management/commands/test_email.py +4 -10
  146. django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +16 -13
  147. django_cfg/modules/django_telegram/management/commands/test_telegram.py +4 -11
  148. django_cfg/modules/django_twilio/management/commands/test_twilio.py +4 -11
  149. django_cfg/modules/django_unfold/navigation.py +6 -18
  150. django_cfg/pyproject.toml +1 -1
  151. django_cfg/registry/modules.py +1 -4
  152. django_cfg/requirements.txt +52 -0
  153. django_cfg/static/frontend/admin.zip +0 -0
  154. {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/METADATA +1 -1
  155. {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/RECORD +158 -121
  156. django_cfg/apps/integrations/grpc/auth/jwt_auth.py +0 -295
  157. {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/WHEEL +0 -0
  158. {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/entry_points.txt +0 -0
  159. {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/licenses/LICENSE +0 -0
@@ -1,98 +1,106 @@
1
1
  """
2
- Pydantic serializers for gRPC Testing API.
2
+ DRF serializers for gRPC Testing API.
3
3
 
4
4
  These serializers define the structure for testing endpoints
5
5
  that provide example payloads and test logs.
6
6
  """
7
7
 
8
- from typing import Any, Dict, List, Optional
8
+ from rest_framework import serializers
9
9
 
10
- from pydantic import BaseModel, Field
11
10
 
12
-
13
- class GRPCExampleSerializer(BaseModel):
11
+ class GRPCExampleSerializer(serializers.Serializer):
14
12
  """Example payload for a gRPC method."""
15
13
 
16
- service: str = Field(..., description="Service name")
17
- method: str = Field(..., description="Method name")
18
- description: str = Field(..., description="Method description")
19
- payload_example: Dict[str, Any] = Field(
20
- ..., description="Example request payload"
21
- )
22
- expected_response: Dict[str, Any] = Field(
23
- ..., description="Example expected response"
24
- )
25
- metadata_example: Dict[str, str] = Field(
26
- default_factory=dict, description="Example metadata (headers)"
14
+ service = serializers.CharField(help_text="Service name")
15
+ method = serializers.CharField(help_text="Method name")
16
+ description = serializers.CharField(help_text="Method description")
17
+ payload_example = serializers.DictField(help_text="Example request payload")
18
+ expected_response = serializers.DictField(help_text="Example expected response")
19
+ metadata_example = serializers.DictField(
20
+ default=dict, help_text="Example metadata (headers)"
27
21
  )
28
22
 
29
23
 
30
- class GRPCExamplesListSerializer(BaseModel):
24
+ class GRPCExamplesListSerializer(serializers.Serializer):
31
25
  """List of examples response."""
32
26
 
33
- examples: List[GRPCExampleSerializer] = Field(
34
- default_factory=list, description="List of examples"
27
+ examples = GRPCExampleSerializer(
28
+ many=True, default=list, help_text="List of examples"
35
29
  )
36
- total_examples: int = Field(..., description="Total number of examples")
30
+ total_examples = serializers.IntegerField(help_text="Total number of examples")
37
31
 
38
32
 
39
- class GRPCTestLogSerializer(BaseModel):
33
+ class GRPCTestLogSerializer(serializers.Serializer):
40
34
  """Single test log entry."""
41
35
 
42
- request_id: str = Field(..., description="Request ID")
43
- service: str = Field(..., description="Service name")
44
- method: str = Field(..., description="Method name")
45
- status: str = Field(..., description="Request status (success, error, etc.)")
46
- grpc_status_code: Optional[str] = Field(
47
- None, description="gRPC status code if available"
36
+ request_id = serializers.CharField(help_text="Request ID")
37
+ service = serializers.CharField(help_text="Service name")
38
+ method = serializers.CharField(help_text="Method name")
39
+ status = serializers.CharField(
40
+ help_text="Request status (success, error, etc.)"
41
+ )
42
+ grpc_status_code = serializers.CharField(
43
+ required=False, allow_null=True, help_text="gRPC status code if available"
44
+ )
45
+ error_message = serializers.CharField(
46
+ required=False, allow_null=True, help_text="Error message if failed"
47
+ )
48
+ duration_ms = serializers.IntegerField(
49
+ required=False, allow_null=True, help_text="Duration in milliseconds"
50
+ )
51
+ created_at = serializers.CharField(
52
+ help_text="Request timestamp (ISO format)"
53
+ )
54
+ user = serializers.CharField(
55
+ required=False, allow_null=True, help_text="User who made the request"
48
56
  )
49
- error_message: Optional[str] = Field(None, description="Error message if failed")
50
- duration_ms: Optional[int] = Field(None, description="Duration in milliseconds")
51
- created_at: str = Field(..., description="Request timestamp (ISO format)")
52
- user: Optional[str] = Field(None, description="User who made the request")
53
57
 
54
58
 
55
- class GRPCTestLogsSerializer(BaseModel):
59
+ class GRPCTestLogsSerializer(serializers.Serializer):
56
60
  """List of test logs response."""
57
61
 
58
- logs: List[GRPCTestLogSerializer] = Field(
59
- default_factory=list, description="List of test logs"
62
+ logs = GRPCTestLogSerializer(many=True, default=list, help_text="List of test logs")
63
+ count = serializers.IntegerField(help_text="Number of logs returned")
64
+ total_available = serializers.IntegerField(help_text="Total logs available")
65
+ has_more = serializers.BooleanField(
66
+ default=False, help_text="Whether more logs are available"
60
67
  )
61
- count: int = Field(..., description="Number of logs returned")
62
- total_available: int = Field(..., description="Total logs available")
63
- has_more: bool = Field(False, description="Whether more logs are available")
64
68
 
65
69
 
66
- class GRPCCallRequestSerializer(BaseModel):
70
+ class GRPCCallRequestSerializer(serializers.Serializer):
67
71
  """Request to call a gRPC method (for future implementation)."""
68
72
 
69
- service: str = Field(..., description="Service name to call")
70
- method: str = Field(..., description="Method name to call")
71
- payload: Dict[str, Any] = Field(..., description="Request payload")
72
- metadata: Dict[str, str] = Field(
73
- default_factory=dict, description="Request metadata (headers)"
73
+ service = serializers.CharField(help_text="Service name to call")
74
+ method = serializers.CharField(help_text="Method name to call")
75
+ payload = serializers.DictField(help_text="Request payload")
76
+ metadata = serializers.DictField(
77
+ default=dict, help_text="Request metadata (headers)"
78
+ )
79
+ timeout_ms = serializers.IntegerField(
80
+ default=5000, help_text="Request timeout in milliseconds"
74
81
  )
75
- timeout_ms: int = Field(5000, description="Request timeout in milliseconds")
76
82
 
77
83
 
78
- class GRPCCallResponseSerializer(BaseModel):
84
+ class GRPCCallResponseSerializer(serializers.Serializer):
79
85
  """Response from calling a gRPC method."""
80
86
 
81
- success: bool = Field(..., description="Whether call was successful")
82
- request_id: str = Field(..., description="Request ID for tracking")
83
- service: str = Field(..., description="Service name")
84
- method: str = Field(..., description="Method name")
85
- status: str = Field(..., description="Request status")
86
- grpc_status_code: str = Field(..., description="gRPC status code")
87
- duration_ms: int = Field(..., description="Call duration in milliseconds")
88
- response: Optional[str] = Field(
89
- None, description="Response data if successful (JSON string)"
87
+ success = serializers.BooleanField(help_text="Whether call was successful")
88
+ request_id = serializers.CharField(help_text="Request ID for tracking")
89
+ service = serializers.CharField(help_text="Service name")
90
+ method = serializers.CharField(help_text="Method name")
91
+ status = serializers.CharField(help_text="Request status")
92
+ grpc_status_code = serializers.CharField(help_text="gRPC status code")
93
+ duration_ms = serializers.IntegerField(help_text="Call duration in milliseconds")
94
+ response = serializers.CharField(
95
+ required=False,
96
+ allow_null=True,
97
+ help_text="Response data if successful (JSON string)",
90
98
  )
91
- error: Optional[str] = Field(None, description="Error message if failed")
92
- metadata: Dict[str, str] = Field(
93
- default_factory=dict, description="Response metadata"
99
+ error = serializers.CharField(
100
+ required=False, allow_null=True, help_text="Error message if failed"
94
101
  )
95
- timestamp: str = Field(..., description="Response timestamp (ISO format)")
102
+ metadata = serializers.DictField(default=dict, help_text="Response metadata")
103
+ timestamp = serializers.CharField(help_text="Response timestamp (ISO format)")
96
104
 
97
105
 
98
106
  __all__ = [
@@ -17,6 +17,7 @@ from .config_helper import (
17
17
  from .discovery import ServiceDiscovery, discover_and_register_services
18
18
  from .grpc_client import DynamicGRPCClient
19
19
  from .monitoring_service import MonitoringService
20
+ from .proto_files_manager import ProtoFilesManager
20
21
  from .service_registry import ServiceRegistryManager
21
22
  from .testing_service import TestingService
22
23
 
@@ -30,6 +31,7 @@ __all__ = [
30
31
  "MonitoringService",
31
32
  "TestingService",
32
33
  "DynamicGRPCClient",
34
+ "ProtoFilesManager",
33
35
  "get_grpc_config",
34
36
  "get_grpc_config_or_default",
35
37
  "is_grpc_enabled",
@@ -53,6 +53,10 @@ class ServiceDiscovery:
53
53
  # Get config from django-cfg using Pydantic2 pattern
54
54
  self.config = get_grpc_config()
55
55
 
56
+ logger.warning(f"🔍 ServiceDiscovery.__init__: config = {self.config}")
57
+ if self.config:
58
+ logger.warning(f"🔍 handlers_hook = {self.config.handlers_hook}")
59
+
56
60
  if self.config:
57
61
  self.auto_register = self.config.auto_register_apps
58
62
  self.enabled_apps = self.config.enabled_apps if self.config.auto_register_apps else []
@@ -452,11 +456,13 @@ class ServiceDiscovery:
452
456
  >>> if handlers_hook:
453
457
  ... services = handlers_hook(server)
454
458
  """
459
+ logger.warning(f"🔍 get_handlers_hook called")
455
460
  if not self.config:
456
- logger.debug("No gRPC config available")
461
+ logger.warning("No gRPC config available")
457
462
  return None
458
463
 
459
464
  handlers_hook_path = self.config.handlers_hook
465
+ logger.warning(f"🔍 handlers_hook_path = '{handlers_hook_path}'")
460
466
 
461
467
  if not handlers_hook_path:
462
468
  logger.debug("No handlers_hook configured")
@@ -7,20 +7,13 @@ Provides business logic for gRPC monitoring and statistics.
7
7
  from datetime import datetime
8
8
  from typing import Dict, List, Optional
9
9
 
10
- from django.conf import settings
11
10
  from django.db import models
12
11
  from django.db.models import Avg, Count, Max
13
12
  from django.db.models.functions import TruncDay, TruncHour
14
13
  from django_cfg.modules.django_logging import get_logger
15
14
 
16
15
  from ..models import GRPCRequestLog, GRPCServerStatus
17
- from ..serializers import (
18
- GRPCHealthCheckSerializer,
19
- GRPCOverviewStatsSerializer,
20
- MethodStatsSerializer,
21
- MonitoringServiceStatsSerializer,
22
- )
23
- from ..serializers.service_registry import RecentRequestSerializer
16
+ from .config_helper import get_grpc_server_config
24
17
 
25
18
  logger = get_logger("grpc.monitoring_service")
26
19
 
@@ -45,7 +38,7 @@ class MonitoringService:
45
38
  >>> health['status']
46
39
  'healthy'
47
40
  """
48
- grpc_server_config = getattr(settings, "GRPC_SERVER", {})
41
+ grpc_server_config = get_grpc_server_config()
49
42
 
50
43
  if not grpc_server_config:
51
44
  raise ValueError("gRPC not configured")
@@ -57,39 +50,152 @@ class MonitoringService:
57
50
  # Ensure enabled is always boolean, not None
58
51
  enabled = bool(is_running) if is_running is not None else False
59
52
 
60
- health_data = GRPCHealthCheckSerializer(
61
- status="healthy" if enabled else "stopped",
62
- server_host=grpc_server_config.get("host", "[::]"),
63
- server_port=grpc_server_config.get("port", 50051),
64
- enabled=enabled,
65
- timestamp=datetime.now().isoformat(),
66
- )
67
-
68
- return health_data.model_dump()
53
+ return {
54
+ "status": "healthy" if enabled else "stopped",
55
+ "server_host": grpc_server_config.host,
56
+ "server_port": grpc_server_config.port,
57
+ "enabled": enabled,
58
+ "timestamp": datetime.now().isoformat(),
59
+ }
69
60
 
70
61
  def get_overview_statistics(self, hours: int = 24) -> Dict:
71
62
  """
72
- Get overview statistics for gRPC requests.
63
+ Get overview statistics for gRPC requests with server information.
73
64
 
74
65
  Args:
75
66
  hours: Statistics period in hours (1-168)
76
67
 
77
68
  Returns:
78
- Dictionary with overview statistics
69
+ Dictionary with overview statistics and server info
79
70
 
80
71
  Example:
81
72
  >>> service = MonitoringService()
82
73
  >>> stats = service.get_overview_statistics(hours=24)
83
- >>> stats['total_requests']
74
+ >>> stats['total']
84
75
  1000
76
+ >>> stats['server']['is_running']
77
+ True
85
78
  """
86
79
  hours = min(max(hours, 1), 168) # 1 hour to 1 week
87
80
 
81
+ # Get request statistics
88
82
  stats = GRPCRequestLog.objects.get_statistics(hours=hours)
89
83
  stats["period_hours"] = hours
90
84
 
91
- overview = GRPCOverviewStatsSerializer(**stats)
92
- return overview.model_dump()
85
+ # Get server information
86
+ server_info = self._get_server_info_for_overview(hours=hours)
87
+ stats["server"] = server_info
88
+
89
+ return stats
90
+
91
+ def _get_server_info_for_overview(self, hours: int) -> Dict:
92
+ """
93
+ Get server information for overview endpoint.
94
+
95
+ Args:
96
+ hours: Statistics period for service stats
97
+
98
+ Returns:
99
+ Dictionary with server information including services
100
+ """
101
+ from ..services import ServiceDiscovery
102
+
103
+ # Get current server status
104
+ current_server = GRPCServerStatus.objects.get_current_server()
105
+
106
+ if not current_server:
107
+ # No server running
108
+ grpc_server_config = get_grpc_server_config()
109
+ return {
110
+ "status": "stopped",
111
+ "is_running": False,
112
+ "host": grpc_server_config.host if grpc_server_config else "[::]",
113
+ "port": grpc_server_config.port if grpc_server_config else 50051,
114
+ "address": f"{grpc_server_config.host}:{grpc_server_config.port}" if grpc_server_config else "[::]:50051",
115
+ "pid": None,
116
+ "started_at": None,
117
+ "uptime_seconds": 0,
118
+ "uptime_display": "Not running",
119
+ "registered_services_count": 0,
120
+ "enable_reflection": False,
121
+ "enable_health_check": False,
122
+ "last_heartbeat": None,
123
+ "services": [],
124
+ "services_healthy": True,
125
+ }
126
+
127
+ # Get service statistics for the period
128
+ service_stats_qs = (
129
+ GRPCRequestLog.objects.recent(hours)
130
+ .values("service_name")
131
+ .annotate(
132
+ total=Count("id"),
133
+ errors=Count("id", filter=models.Q(status="error")),
134
+ )
135
+ )
136
+
137
+ # Create service stats lookup
138
+ service_stats_lookup = {
139
+ stat["service_name"]: stat for stat in service_stats_qs
140
+ }
141
+
142
+ # Get registered services from service discovery
143
+ discovery = ServiceDiscovery()
144
+ registered_services = discovery.get_registered_services()
145
+
146
+ # Build services list with stats
147
+ services_list = []
148
+ total_errors = 0
149
+
150
+ for service in registered_services:
151
+ service_name = service.get("name", "")
152
+ full_name = service.get("full_name", service_name)
153
+ methods = service.get("methods", [])
154
+
155
+ # Get stats for this service
156
+ stats = service_stats_lookup.get(full_name, {"total": 0, "errors": 0})
157
+ request_count = stats.get("total", 0)
158
+ error_count = stats.get("errors", 0)
159
+ success_rate = (
160
+ ((request_count - error_count) / request_count * 100)
161
+ if request_count > 0
162
+ else 100.0
163
+ )
164
+
165
+ total_errors += error_count
166
+
167
+ services_list.append({
168
+ "name": service_name,
169
+ "full_name": full_name,
170
+ "methods_count": len(methods),
171
+ "request_count": request_count,
172
+ "error_count": error_count,
173
+ "success_rate": round(success_rate, 2),
174
+ })
175
+
176
+ # Sort by request count
177
+ services_list.sort(key=lambda x: x["request_count"], reverse=True)
178
+
179
+ # Determine if all services are healthy (no errors in period)
180
+ services_healthy = total_errors == 0
181
+
182
+ return {
183
+ "status": current_server.status,
184
+ "is_running": current_server.is_running,
185
+ "host": current_server.host,
186
+ "port": current_server.port,
187
+ "address": current_server.address,
188
+ "pid": current_server.pid,
189
+ "started_at": current_server.started_at,
190
+ "uptime_seconds": current_server.uptime_seconds,
191
+ "uptime_display": current_server.uptime_display,
192
+ "registered_services_count": len(services_list),
193
+ "enable_reflection": current_server.enable_reflection,
194
+ "enable_health_check": current_server.enable_health_check,
195
+ "last_heartbeat": current_server.last_heartbeat,
196
+ "services": services_list,
197
+ "services_healthy": services_healthy,
198
+ }
93
199
 
94
200
  def get_recent_requests(
95
201
  self,
@@ -114,7 +220,7 @@ class MonitoringService:
114
220
  >>> queryset.count()
115
221
  25
116
222
  """
117
- queryset = GRPCRequestLog.objects.all()
223
+ queryset = GRPCRequestLog.objects.select_related("user", "api_key").all()
118
224
 
119
225
  # Apply filters
120
226
  if service_name:
@@ -160,19 +266,19 @@ class MonitoringService:
160
266
 
161
267
  services_list = []
162
268
  for stats in service_stats:
163
- service_data = MonitoringServiceStatsSerializer(
164
- service_name=stats["service_name"],
165
- total=stats["total"],
166
- successful=stats["successful"],
167
- errors=stats["errors"],
168
- avg_duration_ms=round(stats["avg_duration_ms"] or 0, 2),
169
- last_activity_at=(
269
+ service_data = {
270
+ "service_name": stats["service_name"],
271
+ "total": stats["total"],
272
+ "successful": stats["successful"],
273
+ "errors": stats["errors"],
274
+ "avg_duration_ms": round(stats["avg_duration_ms"] or 0, 2),
275
+ "last_activity_at": (
170
276
  stats["last_activity_at"].isoformat()
171
277
  if stats["last_activity_at"]
172
278
  else None
173
279
  ),
174
- )
175
- services_list.append(service_data.model_dump())
280
+ }
281
+ services_list.append(service_data)
176
282
 
177
283
  return services_list
178
284
 
@@ -217,20 +323,20 @@ class MonitoringService:
217
323
 
218
324
  methods_list = []
219
325
  for stats in method_stats:
220
- method_data = MethodStatsSerializer(
221
- service_name=stats["service_name"],
222
- method_name=stats["method_name"],
223
- total=stats["total"],
224
- successful=stats["successful"],
225
- errors=stats["errors"],
226
- avg_duration_ms=round(stats["avg_duration_ms"] or 0, 2),
227
- last_activity_at=(
326
+ method_data = {
327
+ "service_name": stats["service_name"],
328
+ "method_name": stats["method_name"],
329
+ "total": stats["total"],
330
+ "successful": stats["successful"],
331
+ "errors": stats["errors"],
332
+ "avg_duration_ms": round(stats["avg_duration_ms"] or 0, 2),
333
+ "last_activity_at": (
228
334
  stats["last_activity_at"].isoformat()
229
335
  if stats["last_activity_at"]
230
336
  else None
231
337
  ),
232
- )
233
- methods_list.append(method_data.model_dump())
338
+ }
339
+ methods_list.append(method_data)
234
340
 
235
341
  return methods_list
236
342