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
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gRPC API Keys ViewSet.
|
|
3
|
+
|
|
4
|
+
Provides REST API endpoints for viewing API keys.
|
|
5
|
+
Create/Update/Delete operations handled through Django Admin.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from django.db.models import Count, Q, Sum
|
|
9
|
+
from django.utils import timezone
|
|
10
|
+
from django_cfg.mixins import AdminAPIMixin
|
|
11
|
+
from django_cfg.modules.django_logging import get_logger
|
|
12
|
+
from drf_spectacular.types import OpenApiTypes
|
|
13
|
+
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
|
14
|
+
from rest_framework import status, viewsets
|
|
15
|
+
from rest_framework.decorators import action
|
|
16
|
+
from rest_framework.response import Response
|
|
17
|
+
|
|
18
|
+
from ..models import GrpcApiKey
|
|
19
|
+
from ..serializers.api_keys import (
|
|
20
|
+
ApiKeyListSerializer,
|
|
21
|
+
ApiKeySerializer,
|
|
22
|
+
ApiKeyStatsSerializer,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
logger = get_logger("grpc.api_keys")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class GRPCApiKeyViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
29
|
+
"""
|
|
30
|
+
ViewSet for gRPC API Keys (Read-Only).
|
|
31
|
+
|
|
32
|
+
Provides listing and statistics for API keys.
|
|
33
|
+
Create/Update/Delete operations are handled through Django Admin.
|
|
34
|
+
|
|
35
|
+
Requires admin authentication (JWT, Session, or Basic Auth).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
queryset = GrpcApiKey.objects.none()
|
|
39
|
+
serializer_class = ApiKeySerializer
|
|
40
|
+
|
|
41
|
+
@extend_schema(
|
|
42
|
+
tags=["gRPC API Keys"],
|
|
43
|
+
summary="List API keys",
|
|
44
|
+
description="Returns a list of all API keys with their details. Uses standard DRF pagination.",
|
|
45
|
+
parameters=[
|
|
46
|
+
OpenApiParameter(
|
|
47
|
+
name="is_active",
|
|
48
|
+
type=OpenApiTypes.BOOL,
|
|
49
|
+
location=OpenApiParameter.QUERY,
|
|
50
|
+
description="Filter by active status",
|
|
51
|
+
required=False,
|
|
52
|
+
),
|
|
53
|
+
OpenApiParameter(
|
|
54
|
+
name="key_type",
|
|
55
|
+
type=OpenApiTypes.STR,
|
|
56
|
+
location=OpenApiParameter.QUERY,
|
|
57
|
+
description="Filter by key type (service, cli, webhook, internal, development)",
|
|
58
|
+
required=False,
|
|
59
|
+
),
|
|
60
|
+
OpenApiParameter(
|
|
61
|
+
name="user_id",
|
|
62
|
+
type=OpenApiTypes.INT,
|
|
63
|
+
location=OpenApiParameter.QUERY,
|
|
64
|
+
description="Filter by user ID",
|
|
65
|
+
required=False,
|
|
66
|
+
),
|
|
67
|
+
],
|
|
68
|
+
responses={
|
|
69
|
+
200: ApiKeySerializer(many=True),
|
|
70
|
+
400: {"description": "Invalid parameters"},
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
def list(self, request):
|
|
74
|
+
"""List all API keys."""
|
|
75
|
+
try:
|
|
76
|
+
# Build queryset with filters
|
|
77
|
+
queryset = GrpcApiKey.objects.select_related("user", "created_by").all()
|
|
78
|
+
|
|
79
|
+
# Apply filters
|
|
80
|
+
is_active = request.GET.get("is_active")
|
|
81
|
+
if is_active is not None:
|
|
82
|
+
is_active_bool = is_active.lower() in ("true", "1", "yes")
|
|
83
|
+
queryset = queryset.filter(is_active=is_active_bool)
|
|
84
|
+
|
|
85
|
+
key_type = request.GET.get("key_type")
|
|
86
|
+
if key_type:
|
|
87
|
+
queryset = queryset.filter(key_type=key_type)
|
|
88
|
+
|
|
89
|
+
user_id = request.GET.get("user_id")
|
|
90
|
+
if user_id:
|
|
91
|
+
queryset = queryset.filter(user_id=user_id)
|
|
92
|
+
|
|
93
|
+
# Order by most recently created
|
|
94
|
+
queryset = queryset.order_by("-created_at")
|
|
95
|
+
|
|
96
|
+
# Paginate
|
|
97
|
+
page = self.paginate_queryset(queryset)
|
|
98
|
+
if page is not None:
|
|
99
|
+
# Serialize paginated data
|
|
100
|
+
results = []
|
|
101
|
+
for key in page:
|
|
102
|
+
results.append({
|
|
103
|
+
"id": key.id,
|
|
104
|
+
"name": key.name,
|
|
105
|
+
"key_type": key.key_type,
|
|
106
|
+
"masked_key": key.masked_key,
|
|
107
|
+
"is_active": key.is_active,
|
|
108
|
+
"is_valid": key.is_valid,
|
|
109
|
+
"user_id": key.user.id,
|
|
110
|
+
"username": key.user.username,
|
|
111
|
+
"user_email": key.user.email,
|
|
112
|
+
"request_count": key.request_count,
|
|
113
|
+
"last_used_at": key.last_used_at,
|
|
114
|
+
"expires_at": key.expires_at,
|
|
115
|
+
"created_at": key.created_at,
|
|
116
|
+
"created_by": key.created_by.username if key.created_by else None,
|
|
117
|
+
})
|
|
118
|
+
return self.get_paginated_response(results)
|
|
119
|
+
|
|
120
|
+
# No pagination fallback
|
|
121
|
+
results = []
|
|
122
|
+
for key in queryset[:100]:
|
|
123
|
+
results.append({
|
|
124
|
+
"id": key.id,
|
|
125
|
+
"name": key.name,
|
|
126
|
+
"key_type": key.key_type,
|
|
127
|
+
"masked_key": key.masked_key,
|
|
128
|
+
"is_active": key.is_active,
|
|
129
|
+
"is_valid": key.is_valid,
|
|
130
|
+
"user_id": key.user.id,
|
|
131
|
+
"username": key.user.username,
|
|
132
|
+
"user_email": key.user.email,
|
|
133
|
+
"request_count": key.request_count,
|
|
134
|
+
"last_used_at": key.last_used_at,
|
|
135
|
+
"expires_at": key.expires_at,
|
|
136
|
+
"created_at": key.created_at,
|
|
137
|
+
"created_by": key.created_by.username if key.created_by else None,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
return Response({"results": results, "count": len(results)})
|
|
141
|
+
|
|
142
|
+
except ValueError as e:
|
|
143
|
+
logger.warning(f"API keys list validation error: {e}")
|
|
144
|
+
return Response(
|
|
145
|
+
{"error": str(e)}, status=status.HTTP_400_BAD_REQUEST
|
|
146
|
+
)
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.error(f"API keys list error: {e}", exc_info=True)
|
|
149
|
+
return Response(
|
|
150
|
+
{"error": "Internal server error"},
|
|
151
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
@extend_schema(
|
|
155
|
+
tags=["gRPC API Keys"],
|
|
156
|
+
summary="Get API key details",
|
|
157
|
+
description="Returns detailed information about a specific API key.",
|
|
158
|
+
responses={
|
|
159
|
+
200: ApiKeySerializer,
|
|
160
|
+
404: {"description": "API key not found"},
|
|
161
|
+
},
|
|
162
|
+
)
|
|
163
|
+
def retrieve(self, request, pk=None):
|
|
164
|
+
"""Get details of a specific API key."""
|
|
165
|
+
try:
|
|
166
|
+
key = GrpcApiKey.objects.select_related("user", "created_by").get(pk=pk)
|
|
167
|
+
|
|
168
|
+
data = {
|
|
169
|
+
"id": key.id,
|
|
170
|
+
"name": key.name,
|
|
171
|
+
"key_type": key.key_type,
|
|
172
|
+
"masked_key": key.masked_key,
|
|
173
|
+
"is_active": key.is_active,
|
|
174
|
+
"is_valid": key.is_valid,
|
|
175
|
+
"user_id": key.user.id,
|
|
176
|
+
"username": key.user.username,
|
|
177
|
+
"user_email": key.user.email,
|
|
178
|
+
"request_count": key.request_count,
|
|
179
|
+
"last_used_at": key.last_used_at,
|
|
180
|
+
"expires_at": key.expires_at,
|
|
181
|
+
"created_at": key.created_at,
|
|
182
|
+
"created_by": key.created_by.username if key.created_by else None,
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
serializer = ApiKeySerializer(data=data)
|
|
186
|
+
serializer.is_valid(raise_exception=True)
|
|
187
|
+
return Response(serializer.data)
|
|
188
|
+
|
|
189
|
+
except GrpcApiKey.DoesNotExist:
|
|
190
|
+
return Response(
|
|
191
|
+
{"error": "API key not found"},
|
|
192
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
193
|
+
)
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(f"API key retrieve error: {e}", exc_info=True)
|
|
196
|
+
return Response(
|
|
197
|
+
{"error": "Internal server error"},
|
|
198
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
@extend_schema(
|
|
202
|
+
tags=["gRPC API Keys"],
|
|
203
|
+
summary="Get API keys statistics",
|
|
204
|
+
description="Returns overall statistics about API keys usage.",
|
|
205
|
+
responses={
|
|
206
|
+
200: ApiKeyStatsSerializer,
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
@action(detail=False, methods=["get"], url_path="stats")
|
|
210
|
+
def stats(self, request):
|
|
211
|
+
"""Get API keys statistics."""
|
|
212
|
+
try:
|
|
213
|
+
# Total keys
|
|
214
|
+
total_keys = GrpcApiKey.objects.count()
|
|
215
|
+
|
|
216
|
+
# Active keys
|
|
217
|
+
active_keys = GrpcApiKey.objects.filter(is_active=True).count()
|
|
218
|
+
|
|
219
|
+
# Expired keys
|
|
220
|
+
expired_keys = GrpcApiKey.objects.filter(
|
|
221
|
+
Q(is_active=True) & Q(expires_at__lt=timezone.now())
|
|
222
|
+
).count()
|
|
223
|
+
|
|
224
|
+
# Total requests across all keys
|
|
225
|
+
total_requests = GrpcApiKey.objects.aggregate(
|
|
226
|
+
total=Sum("request_count")
|
|
227
|
+
)["total"] or 0
|
|
228
|
+
|
|
229
|
+
# Keys by type
|
|
230
|
+
keys_by_type_qs = GrpcApiKey.objects.values("key_type").annotate(
|
|
231
|
+
count=Count("id")
|
|
232
|
+
)
|
|
233
|
+
keys_by_type = {item["key_type"]: item["count"] for item in keys_by_type_qs}
|
|
234
|
+
|
|
235
|
+
stats_data = {
|
|
236
|
+
"total_keys": total_keys,
|
|
237
|
+
"active_keys": active_keys,
|
|
238
|
+
"expired_keys": expired_keys,
|
|
239
|
+
"total_requests": total_requests,
|
|
240
|
+
"keys_by_type": keys_by_type,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
serializer = ApiKeyStatsSerializer(data=stats_data)
|
|
244
|
+
serializer.is_valid(raise_exception=True)
|
|
245
|
+
return Response(serializer.data)
|
|
246
|
+
|
|
247
|
+
except Exception as e:
|
|
248
|
+
logger.error(f"API keys stats error: {e}", exc_info=True)
|
|
249
|
+
return Response(
|
|
250
|
+
{"error": "Internal server error"},
|
|
251
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
__all__ = ["GRPCApiKeyViewSet"]
|
|
@@ -72,8 +72,9 @@ class GRPCChartsViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
72
72
|
try:
|
|
73
73
|
hours = self._validate_hours(request.GET.get("hours", "24"))
|
|
74
74
|
data = ChartGeneratorService.generate_server_uptime_data(hours)
|
|
75
|
-
serializer = ServerUptimeChartSerializer(
|
|
76
|
-
|
|
75
|
+
serializer = ServerUptimeChartSerializer(data=data)
|
|
76
|
+
serializer.is_valid(raise_exception=True)
|
|
77
|
+
return Response(serializer.data)
|
|
77
78
|
except Exception as e:
|
|
78
79
|
logger.error(f"Server uptime chart error: {e}", exc_info=True)
|
|
79
80
|
return Response(
|
|
@@ -94,8 +95,9 @@ class GRPCChartsViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
94
95
|
try:
|
|
95
96
|
hours = self._validate_hours(request.GET.get("hours", "24"))
|
|
96
97
|
data = ChartGeneratorService.generate_request_volume_data(hours)
|
|
97
|
-
serializer = RequestVolumeChartSerializer(
|
|
98
|
-
|
|
98
|
+
serializer = RequestVolumeChartSerializer(data=data)
|
|
99
|
+
serializer.is_valid(raise_exception=True)
|
|
100
|
+
return Response(serializer.data)
|
|
99
101
|
except Exception as e:
|
|
100
102
|
logger.error(f"Request volume chart error: {e}", exc_info=True)
|
|
101
103
|
return Response(
|
|
@@ -116,8 +118,9 @@ class GRPCChartsViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
116
118
|
try:
|
|
117
119
|
hours = self._validate_hours(request.GET.get("hours", "24"))
|
|
118
120
|
data = ChartGeneratorService.generate_response_time_data(hours)
|
|
119
|
-
serializer = ResponseTimeChartSerializer(
|
|
120
|
-
|
|
121
|
+
serializer = ResponseTimeChartSerializer(data=data)
|
|
122
|
+
serializer.is_valid(raise_exception=True)
|
|
123
|
+
return Response(serializer.data)
|
|
121
124
|
except Exception as e:
|
|
122
125
|
logger.error(f"Response time chart error: {e}", exc_info=True)
|
|
123
126
|
return Response(
|
|
@@ -138,8 +141,9 @@ class GRPCChartsViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
138
141
|
try:
|
|
139
142
|
hours = self._validate_hours(request.GET.get("hours", "24"))
|
|
140
143
|
data = ChartGeneratorService.generate_service_activity_data(hours)
|
|
141
|
-
serializer = ServiceActivityChartSerializer(
|
|
142
|
-
|
|
144
|
+
serializer = ServiceActivityChartSerializer(data=data)
|
|
145
|
+
serializer.is_valid(raise_exception=True)
|
|
146
|
+
return Response(serializer.data)
|
|
143
147
|
except Exception as e:
|
|
144
148
|
logger.error(f"Service activity chart error: {e}", exc_info=True)
|
|
145
149
|
return Response(
|
|
@@ -160,8 +164,9 @@ class GRPCChartsViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
160
164
|
try:
|
|
161
165
|
hours = self._validate_hours(request.GET.get("hours", "24"))
|
|
162
166
|
data = ChartGeneratorService.generate_server_lifecycle_data(hours)
|
|
163
|
-
serializer = ServerLifecycleChartSerializer(
|
|
164
|
-
|
|
167
|
+
serializer = ServerLifecycleChartSerializer(data=data)
|
|
168
|
+
serializer.is_valid(raise_exception=True)
|
|
169
|
+
return Response(serializer.data)
|
|
165
170
|
except Exception as e:
|
|
166
171
|
logger.error(f"Server lifecycle chart error: {e}", exc_info=True)
|
|
167
172
|
return Response(
|
|
@@ -182,8 +187,9 @@ class GRPCChartsViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
182
187
|
try:
|
|
183
188
|
hours = self._validate_hours(request.GET.get("hours", "24"))
|
|
184
189
|
data = ChartGeneratorService.generate_error_distribution_data(hours)
|
|
185
|
-
serializer = ErrorDistributionChartSerializer(
|
|
186
|
-
|
|
190
|
+
serializer = ErrorDistributionChartSerializer(data=data)
|
|
191
|
+
serializer.is_valid(raise_exception=True)
|
|
192
|
+
return Response(serializer.data)
|
|
187
193
|
except Exception as e:
|
|
188
194
|
logger.error(f"Error distribution chart error: {e}", exc_info=True)
|
|
189
195
|
return Response(
|
|
@@ -204,8 +210,9 @@ class GRPCChartsViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
204
210
|
try:
|
|
205
211
|
hours = self._validate_hours(request.GET.get("hours", "24"))
|
|
206
212
|
data = ChartGeneratorService.generate_dashboard_data(hours)
|
|
207
|
-
serializer = DashboardChartsSerializer(
|
|
208
|
-
|
|
213
|
+
serializer = DashboardChartsSerializer(data=data)
|
|
214
|
+
serializer.is_valid(raise_exception=True)
|
|
215
|
+
return Response(serializer.data)
|
|
209
216
|
except Exception as e:
|
|
210
217
|
logger.error(f"Dashboard charts error: {e}", exc_info=True)
|
|
211
218
|
return Response(
|
|
@@ -67,7 +67,7 @@ class GRPCConfigViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
67
67
|
"host": grpc_config.server.host,
|
|
68
68
|
"port": grpc_config.server.port,
|
|
69
69
|
"enabled": grpc_config.server.enabled,
|
|
70
|
-
"
|
|
70
|
+
"max_concurrent_streams": grpc_config.server.max_concurrent_streams,
|
|
71
71
|
"max_concurrent_rpcs": None, # Not in current config
|
|
72
72
|
},
|
|
73
73
|
"framework": {
|
|
@@ -77,7 +77,7 @@ class GRPCConfigViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
77
77
|
"interceptors": grpc_config.server.interceptors,
|
|
78
78
|
},
|
|
79
79
|
"features": {
|
|
80
|
-
"
|
|
80
|
+
"api_key_auth": grpc_config.auth.enabled,
|
|
81
81
|
"request_logging": True, # Always on
|
|
82
82
|
"metrics": True, # Always on
|
|
83
83
|
"reflection": grpc_config.server.enable_reflection,
|
|
@@ -86,8 +86,9 @@ class GRPCConfigViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
86
86
|
"total_methods": methods_count,
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
serializer = GRPCConfigSerializer(
|
|
90
|
-
|
|
89
|
+
serializer = GRPCConfigSerializer(data=config_data)
|
|
90
|
+
serializer.is_valid(raise_exception=True)
|
|
91
|
+
return Response(serializer.data)
|
|
91
92
|
|
|
92
93
|
except Exception as e:
|
|
93
94
|
logger.error(f"Config fetch error: {e}", exc_info=True)
|
|
@@ -177,8 +178,9 @@ class GRPCConfigViewSet(AdminAPIMixin, viewsets.ViewSet):
|
|
|
177
178
|
},
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
serializer = GRPCServerInfoSerializer(
|
|
181
|
-
|
|
181
|
+
serializer = GRPCServerInfoSerializer(data=server_info_data)
|
|
182
|
+
serializer.is_valid(raise_exception=True)
|
|
183
|
+
return Response(serializer.data)
|
|
182
184
|
|
|
183
185
|
except Exception as e:
|
|
184
186
|
logger.error(f"Server info error: {e}", exc_info=True)
|
|
@@ -11,6 +11,7 @@ from django.db import models
|
|
|
11
11
|
from django.db.models import Avg, Count, Max
|
|
12
12
|
from django.db.models.functions import TruncDay, TruncHour
|
|
13
13
|
from django_cfg.mixins import AdminAPIMixin
|
|
14
|
+
from django_cfg.middleware.pagination import DefaultPagination
|
|
14
15
|
from django_cfg.modules.django_logging import get_logger
|
|
15
16
|
from drf_spectacular.types import OpenApiTypes
|
|
16
17
|
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
|
@@ -24,8 +25,6 @@ from ..serializers import (
|
|
|
24
25
|
GRPCOverviewStatsSerializer,
|
|
25
26
|
MethodListSerializer,
|
|
26
27
|
MethodStatsSerializer,
|
|
27
|
-
MonitoringServiceStatsSerializer,
|
|
28
|
-
ServiceListSerializer,
|
|
29
28
|
)
|
|
30
29
|
from ..serializers.service_registry import RecentRequestSerializer
|
|
31
30
|
from ..services import MonitoringService
|
|
@@ -48,6 +47,8 @@ class GRPCMonitorViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
|
48
47
|
Requires admin authentication (JWT, Session, or Basic Auth).
|
|
49
48
|
"""
|
|
50
49
|
|
|
50
|
+
pagination_class = DefaultPagination
|
|
51
|
+
|
|
51
52
|
# Required for GenericViewSet
|
|
52
53
|
queryset = GRPCRequestLog.objects.none() # Placeholder, actual queries in actions
|
|
53
54
|
serializer_class = RecentRequestSerializer # Default serializer for schema
|
|
@@ -67,7 +68,9 @@ class GRPCMonitorViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
|
67
68
|
try:
|
|
68
69
|
service = MonitoringService()
|
|
69
70
|
health_data = service.get_health_status()
|
|
70
|
-
|
|
71
|
+
serializer = GRPCHealthCheckSerializer(data=health_data)
|
|
72
|
+
serializer.is_valid(raise_exception=True)
|
|
73
|
+
return Response(serializer.data)
|
|
71
74
|
|
|
72
75
|
except ValueError as e:
|
|
73
76
|
return Response(
|
|
@@ -107,7 +110,9 @@ class GRPCMonitorViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
|
107
110
|
|
|
108
111
|
service = MonitoringService()
|
|
109
112
|
stats = service.get_overview_statistics(hours=hours)
|
|
110
|
-
|
|
113
|
+
serializer = GRPCOverviewStatsSerializer(data=stats)
|
|
114
|
+
serializer.is_valid(raise_exception=True)
|
|
115
|
+
return Response(serializer.data)
|
|
111
116
|
|
|
112
117
|
except ValueError as e:
|
|
113
118
|
logger.warning(f"Overview stats validation error: {e}")
|
|
@@ -149,7 +154,7 @@ class GRPCMonitorViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
|
149
154
|
),
|
|
150
155
|
],
|
|
151
156
|
responses={
|
|
152
|
-
200: RecentRequestSerializer,
|
|
157
|
+
200: RecentRequestSerializer(many=True), # Use many=True for paginated list
|
|
153
158
|
400: {"description": "Invalid parameters"},
|
|
154
159
|
},
|
|
155
160
|
)
|
|
@@ -174,37 +179,49 @@ class GRPCMonitorViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
|
174
179
|
# Serialize paginated data
|
|
175
180
|
requests_list = []
|
|
176
181
|
for req in page:
|
|
177
|
-
|
|
178
|
-
id
|
|
179
|
-
request_id
|
|
180
|
-
service_name
|
|
181
|
-
method_name
|
|
182
|
-
status
|
|
183
|
-
duration_ms
|
|
184
|
-
grpc_status_code
|
|
185
|
-
error_message
|
|
186
|
-
created_at
|
|
187
|
-
client_ip
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
requests_list.append({
|
|
183
|
+
"id": req.id,
|
|
184
|
+
"request_id": req.request_id,
|
|
185
|
+
"service_name": req.service_name,
|
|
186
|
+
"method_name": req.method_name,
|
|
187
|
+
"status": req.status,
|
|
188
|
+
"duration_ms": req.duration_ms or 0,
|
|
189
|
+
"grpc_status_code": req.grpc_status_code or "",
|
|
190
|
+
"error_message": req.error_message or "",
|
|
191
|
+
"created_at": req.created_at.isoformat(),
|
|
192
|
+
"client_ip": req.client_ip or "",
|
|
193
|
+
# User information
|
|
194
|
+
"user_id": req.user.id if req.user else None,
|
|
195
|
+
"username": req.user.username if req.user else "",
|
|
196
|
+
"is_authenticated": req.is_authenticated,
|
|
197
|
+
# API Key information
|
|
198
|
+
"api_key_id": req.api_key.id if req.api_key else None,
|
|
199
|
+
"api_key_name": req.api_key.name if req.api_key else "",
|
|
200
|
+
})
|
|
190
201
|
return self.get_paginated_response(requests_list)
|
|
191
202
|
|
|
192
203
|
# No pagination fallback
|
|
193
204
|
requests_list = []
|
|
194
205
|
for req in queryset[:100]:
|
|
195
|
-
|
|
196
|
-
id
|
|
197
|
-
request_id
|
|
198
|
-
service_name
|
|
199
|
-
method_name
|
|
200
|
-
status
|
|
201
|
-
duration_ms
|
|
202
|
-
grpc_status_code
|
|
203
|
-
error_message
|
|
204
|
-
created_at
|
|
205
|
-
client_ip
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
requests_list.append({
|
|
207
|
+
"id": req.id,
|
|
208
|
+
"request_id": req.request_id,
|
|
209
|
+
"service_name": req.service_name,
|
|
210
|
+
"method_name": req.method_name,
|
|
211
|
+
"status": req.status,
|
|
212
|
+
"duration_ms": req.duration_ms or 0,
|
|
213
|
+
"grpc_status_code": req.grpc_status_code or "",
|
|
214
|
+
"error_message": req.error_message or "",
|
|
215
|
+
"created_at": req.created_at.isoformat(),
|
|
216
|
+
"client_ip": req.client_ip or "",
|
|
217
|
+
# User information
|
|
218
|
+
"user_id": req.user.id if req.user else None,
|
|
219
|
+
"username": req.user.username if req.user else "",
|
|
220
|
+
"is_authenticated": req.is_authenticated,
|
|
221
|
+
# API Key information
|
|
222
|
+
"api_key_id": req.api_key.id if req.api_key else None,
|
|
223
|
+
"api_key_name": req.api_key.name if req.api_key else "",
|
|
224
|
+
})
|
|
208
225
|
return Response({"requests": requests_list, "count": len(requests_list)})
|
|
209
226
|
|
|
210
227
|
except ValueError as e:
|
|
@@ -219,52 +236,6 @@ class GRPCMonitorViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
|
219
236
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
220
237
|
)
|
|
221
238
|
|
|
222
|
-
@extend_schema(
|
|
223
|
-
tags=["gRPC Monitoring"],
|
|
224
|
-
summary="Get service statistics",
|
|
225
|
-
description="Returns statistics grouped by service.",
|
|
226
|
-
parameters=[
|
|
227
|
-
OpenApiParameter(
|
|
228
|
-
name="hours",
|
|
229
|
-
type=OpenApiTypes.INT,
|
|
230
|
-
location=OpenApiParameter.QUERY,
|
|
231
|
-
description="Statistics period in hours (default: 24)",
|
|
232
|
-
required=False,
|
|
233
|
-
),
|
|
234
|
-
],
|
|
235
|
-
responses={
|
|
236
|
-
200: ServiceListSerializer,
|
|
237
|
-
400: {"description": "Invalid parameters"},
|
|
238
|
-
},
|
|
239
|
-
)
|
|
240
|
-
@action(detail=False, methods=["get"], url_path="services", pagination_class=None)
|
|
241
|
-
def services(self, request):
|
|
242
|
-
"""Get statistics per service."""
|
|
243
|
-
try:
|
|
244
|
-
hours = int(request.GET.get("hours", 24))
|
|
245
|
-
|
|
246
|
-
service = MonitoringService()
|
|
247
|
-
services_list = service.get_service_statistics(hours=hours)
|
|
248
|
-
|
|
249
|
-
response_data = {
|
|
250
|
-
"services": services_list,
|
|
251
|
-
"total_services": len(services_list),
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return Response(response_data)
|
|
255
|
-
|
|
256
|
-
except ValueError as e:
|
|
257
|
-
logger.warning(f"Service stats validation error: {e}")
|
|
258
|
-
return Response(
|
|
259
|
-
{"error": str(e)}, status=status.HTTP_400_BAD_REQUEST
|
|
260
|
-
)
|
|
261
|
-
except Exception as e:
|
|
262
|
-
logger.error(f"Service stats error: {e}", exc_info=True)
|
|
263
|
-
return Response(
|
|
264
|
-
{"error": "Internal server error"},
|
|
265
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
266
|
-
)
|
|
267
|
-
|
|
268
239
|
@extend_schema(
|
|
269
240
|
tags=["gRPC Monitoring"],
|
|
270
241
|
summary="Get method statistics",
|
|
@@ -308,8 +279,9 @@ class GRPCMonitorViewSet(AdminAPIMixin, viewsets.GenericViewSet):
|
|
|
308
279
|
"total_methods": len(methods_list),
|
|
309
280
|
}
|
|
310
281
|
|
|
311
|
-
serializer = MethodListSerializer(
|
|
312
|
-
|
|
282
|
+
serializer = MethodListSerializer(data=response_data)
|
|
283
|
+
serializer.is_valid(raise_exception=True)
|
|
284
|
+
return Response(serializer.data)
|
|
313
285
|
|
|
314
286
|
except ValueError as e:
|
|
315
287
|
logger.warning(f"Method stats validation error: {e}")
|