django-cfg 1.5.8__py3-none-any.whl → 1.5.14__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/api/commands/serializers.py +152 -0
- django_cfg/apps/api/commands/views.py +32 -0
- django_cfg/apps/business/accounts/management/commands/otp_test.py +5 -2
- django_cfg/apps/business/agents/management/commands/create_agent.py +5 -194
- django_cfg/apps/business/agents/management/commands/load_agent_templates.py +205 -0
- django_cfg/apps/business/agents/management/commands/orchestrator_status.py +4 -2
- django_cfg/apps/business/knowbase/management/commands/knowbase_stats.py +4 -2
- django_cfg/apps/business/knowbase/management/commands/setup_knowbase.py +4 -2
- django_cfg/apps/business/newsletter/management/commands/test_newsletter.py +5 -2
- django_cfg/apps/business/payments/management/commands/check_payment_status.py +4 -2
- django_cfg/apps/business/payments/management/commands/create_payment.py +4 -2
- django_cfg/apps/business/payments/management/commands/sync_currencies.py +4 -2
- django_cfg/apps/integrations/centrifugo/management/commands/generate_centrifugo_clients.py +5 -5
- django_cfg/apps/integrations/centrifugo/serializers/__init__.py +2 -1
- django_cfg/apps/integrations/centrifugo/serializers/publishes.py +22 -2
- django_cfg/apps/integrations/centrifugo/views/monitoring.py +25 -40
- django_cfg/apps/integrations/grpc/admin/__init__.py +7 -1
- django_cfg/apps/integrations/grpc/admin/config.py +113 -9
- django_cfg/apps/integrations/grpc/admin/grpc_api_key.py +129 -0
- django_cfg/apps/integrations/grpc/admin/grpc_request_log.py +72 -63
- django_cfg/apps/integrations/grpc/admin/grpc_server_status.py +236 -0
- django_cfg/apps/integrations/grpc/auth/__init__.py +11 -3
- django_cfg/apps/integrations/grpc/auth/api_key_auth.py +320 -0
- django_cfg/apps/integrations/grpc/interceptors/logging.py +17 -20
- django_cfg/apps/integrations/grpc/interceptors/metrics.py +15 -14
- django_cfg/apps/integrations/grpc/interceptors/request_logger.py +79 -59
- django_cfg/apps/integrations/grpc/management/commands/generate_protos.py +130 -0
- django_cfg/apps/integrations/grpc/management/commands/rungrpc.py +171 -96
- django_cfg/apps/integrations/grpc/management/commands/test_grpc_integration.py +75 -0
- django_cfg/apps/integrations/grpc/managers/__init__.py +2 -0
- django_cfg/apps/integrations/grpc/managers/grpc_api_key.py +192 -0
- django_cfg/apps/integrations/grpc/managers/grpc_server_status.py +19 -11
- django_cfg/apps/integrations/grpc/migrations/0005_grpcapikey.py +143 -0
- django_cfg/apps/integrations/grpc/migrations/0006_grpcrequestlog_api_key_and_more.py +34 -0
- django_cfg/apps/integrations/grpc/models/__init__.py +2 -0
- django_cfg/apps/integrations/grpc/models/grpc_api_key.py +198 -0
- django_cfg/apps/integrations/grpc/models/grpc_request_log.py +11 -0
- django_cfg/apps/integrations/grpc/models/grpc_server_status.py +39 -4
- django_cfg/apps/integrations/grpc/serializers/__init__.py +22 -6
- django_cfg/apps/integrations/grpc/serializers/api_keys.py +63 -0
- django_cfg/apps/integrations/grpc/serializers/charts.py +118 -120
- django_cfg/apps/integrations/grpc/serializers/config.py +65 -51
- django_cfg/apps/integrations/grpc/serializers/health.py +7 -7
- django_cfg/apps/integrations/grpc/serializers/proto_files.py +74 -0
- django_cfg/apps/integrations/grpc/serializers/requests.py +13 -7
- django_cfg/apps/integrations/grpc/serializers/service_registry.py +181 -112
- django_cfg/apps/integrations/grpc/serializers/services.py +14 -32
- django_cfg/apps/integrations/grpc/serializers/stats.py +50 -12
- django_cfg/apps/integrations/grpc/serializers/testing.py +66 -58
- django_cfg/apps/integrations/grpc/services/__init__.py +2 -0
- django_cfg/apps/integrations/grpc/services/monitoring_service.py +149 -43
- django_cfg/apps/integrations/grpc/services/proto_files_manager.py +268 -0
- django_cfg/apps/integrations/grpc/services/service_registry.py +48 -46
- django_cfg/apps/integrations/grpc/services/testing_service.py +10 -15
- django_cfg/apps/integrations/grpc/urls.py +8 -0
- django_cfg/apps/integrations/grpc/utils/__init__.py +4 -13
- django_cfg/apps/integrations/grpc/utils/integration_test.py +334 -0
- django_cfg/apps/integrations/grpc/utils/proto_gen.py +48 -8
- django_cfg/apps/integrations/grpc/utils/streaming_logger.py +177 -0
- django_cfg/apps/integrations/grpc/views/__init__.py +4 -0
- django_cfg/apps/integrations/grpc/views/api_keys.py +255 -0
- django_cfg/apps/integrations/grpc/views/charts.py +21 -14
- django_cfg/apps/integrations/grpc/views/config.py +8 -6
- django_cfg/apps/integrations/grpc/views/monitoring.py +51 -79
- django_cfg/apps/integrations/grpc/views/proto_files.py +214 -0
- django_cfg/apps/integrations/grpc/views/services.py +30 -21
- django_cfg/apps/integrations/grpc/views/testing.py +45 -43
- django_cfg/apps/integrations/rq/views/jobs.py +19 -9
- django_cfg/apps/integrations/rq/views/schedule.py +7 -3
- django_cfg/apps/system/dashboard/serializers/commands.py +25 -1
- django_cfg/apps/system/dashboard/services/commands_service.py +12 -1
- django_cfg/apps/system/maintenance/management/commands/maintenance.py +5 -2
- django_cfg/apps/system/maintenance/management/commands/process_scheduled_maintenance.py +4 -2
- django_cfg/apps/system/maintenance/management/commands/sync_cloudflare.py +5 -2
- django_cfg/config.py +33 -0
- django_cfg/core/generation/integration_generators/grpc_generator.py +30 -32
- django_cfg/management/commands/check_endpoints.py +2 -2
- django_cfg/management/commands/check_settings.py +3 -10
- django_cfg/management/commands/clear_constance.py +3 -10
- django_cfg/management/commands/create_token.py +4 -11
- django_cfg/management/commands/list_urls.py +4 -10
- django_cfg/management/commands/migrate_all.py +18 -12
- django_cfg/management/commands/migrator.py +4 -11
- django_cfg/management/commands/script.py +4 -10
- django_cfg/management/commands/show_config.py +8 -16
- django_cfg/management/commands/show_urls.py +5 -11
- django_cfg/management/commands/superuser.py +4 -11
- django_cfg/management/commands/tree.py +5 -10
- django_cfg/management/utils/README.md +402 -0
- django_cfg/management/utils/__init__.py +29 -0
- django_cfg/management/utils/mixins.py +176 -0
- django_cfg/middleware/pagination.py +53 -54
- django_cfg/models/api/grpc/__init__.py +15 -21
- django_cfg/models/api/grpc/config.py +155 -73
- django_cfg/models/ngrok/config.py +7 -6
- django_cfg/modules/django_client/core/generator/python/files_generator.py +5 -13
- django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +16 -4
- django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +2 -3
- django_cfg/modules/django_client/core/generator/typescript/files_generator.py +6 -5
- django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +12 -8
- django_cfg/modules/django_client/core/parser/base.py +114 -30
- django_cfg/modules/django_client/management/commands/generate_client.py +5 -2
- django_cfg/modules/django_client/management/commands/validate_openapi.py +5 -2
- django_cfg/modules/django_email/management/commands/test_email.py +4 -10
- django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +16 -13
- django_cfg/modules/django_telegram/management/commands/test_telegram.py +4 -11
- django_cfg/modules/django_twilio/management/commands/test_twilio.py +4 -11
- django_cfg/modules/django_unfold/navigation.py +6 -18
- django_cfg/pyproject.toml +1 -1
- django_cfg/registry/modules.py +1 -4
- django_cfg/requirements.txt +52 -0
- django_cfg/static/frontend/admin.zip +0 -0
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/METADATA +1 -1
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/RECORD +118 -97
- django_cfg/apps/integrations/grpc/auth/jwt_auth.py +0 -295
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/WHEEL +0 -0
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.14.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,98 +1,106 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
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
|
|
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
|
|
17
|
-
method
|
|
18
|
-
description
|
|
19
|
-
payload_example
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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(
|
|
24
|
+
class GRPCExamplesListSerializer(serializers.Serializer):
|
|
31
25
|
"""List of examples response."""
|
|
32
26
|
|
|
33
|
-
examples
|
|
34
|
-
|
|
27
|
+
examples = GRPCExampleSerializer(
|
|
28
|
+
many=True, default=list, help_text="List of examples"
|
|
35
29
|
)
|
|
36
|
-
total_examples
|
|
30
|
+
total_examples = serializers.IntegerField(help_text="Total number of examples")
|
|
37
31
|
|
|
38
32
|
|
|
39
|
-
class GRPCTestLogSerializer(
|
|
33
|
+
class GRPCTestLogSerializer(serializers.Serializer):
|
|
40
34
|
"""Single test log entry."""
|
|
41
35
|
|
|
42
|
-
request_id
|
|
43
|
-
service
|
|
44
|
-
method
|
|
45
|
-
status
|
|
46
|
-
|
|
47
|
-
|
|
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(
|
|
59
|
+
class GRPCTestLogsSerializer(serializers.Serializer):
|
|
56
60
|
"""List of test logs response."""
|
|
57
61
|
|
|
58
|
-
logs
|
|
59
|
-
|
|
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(
|
|
70
|
+
class GRPCCallRequestSerializer(serializers.Serializer):
|
|
67
71
|
"""Request to call a gRPC method (for future implementation)."""
|
|
68
72
|
|
|
69
|
-
service
|
|
70
|
-
method
|
|
71
|
-
payload
|
|
72
|
-
metadata
|
|
73
|
-
|
|
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(
|
|
84
|
+
class GRPCCallResponseSerializer(serializers.Serializer):
|
|
79
85
|
"""Response from calling a gRPC method."""
|
|
80
86
|
|
|
81
|
-
success
|
|
82
|
-
request_id
|
|
83
|
-
service
|
|
84
|
-
method
|
|
85
|
-
status
|
|
86
|
-
grpc_status_code
|
|
87
|
-
duration_ms
|
|
88
|
-
response
|
|
89
|
-
|
|
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
|
|
92
|
-
|
|
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
|
-
|
|
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",
|
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
61
|
-
status
|
|
62
|
-
server_host
|
|
63
|
-
server_port
|
|
64
|
-
enabled
|
|
65
|
-
timestamp
|
|
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['
|
|
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
|
-
|
|
92
|
-
|
|
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 =
|
|
164
|
-
service_name
|
|
165
|
-
total
|
|
166
|
-
successful
|
|
167
|
-
errors
|
|
168
|
-
avg_duration_ms
|
|
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
|
|
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 =
|
|
221
|
-
service_name
|
|
222
|
-
method_name
|
|
223
|
-
total
|
|
224
|
-
successful
|
|
225
|
-
errors
|
|
226
|
-
avg_duration_ms
|
|
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
|
|
338
|
+
}
|
|
339
|
+
methods_list.append(method_data)
|
|
234
340
|
|
|
235
341
|
return methods_list
|
|
236
342
|
|