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.
- 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/accounts/serializers/profile.py +42 -0
- 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/business/support/serializers.py +3 -2
- django_cfg/apps/integrations/centrifugo/apps.py +2 -1
- django_cfg/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +151 -12
- django_cfg/apps/integrations/centrifugo/management/commands/generate_centrifugo_clients.py +6 -6
- 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/services/__init__.py +6 -0
- django_cfg/apps/integrations/centrifugo/services/client/__init__.py +6 -1
- django_cfg/apps/integrations/centrifugo/services/client/direct_client.py +282 -0
- django_cfg/apps/integrations/centrifugo/services/publisher.py +371 -0
- django_cfg/apps/integrations/centrifugo/services/token_generator.py +122 -0
- django_cfg/apps/integrations/centrifugo/urls.py +8 -0
- django_cfg/apps/integrations/centrifugo/views/__init__.py +2 -0
- django_cfg/apps/integrations/centrifugo/views/monitoring.py +25 -40
- django_cfg/apps/integrations/centrifugo/views/testing_api.py +0 -79
- django_cfg/apps/integrations/centrifugo/views/token_api.py +101 -0
- django_cfg/apps/integrations/centrifugo/views/wrapper.py +257 -0
- 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/centrifugo/__init__.py +29 -0
- django_cfg/apps/integrations/grpc/centrifugo/bridge.py +277 -0
- django_cfg/apps/integrations/grpc/centrifugo/config.py +167 -0
- django_cfg/apps/integrations/grpc/centrifugo/demo.py +626 -0
- django_cfg/apps/integrations/grpc/centrifugo/test_publish.py +229 -0
- django_cfg/apps/integrations/grpc/centrifugo/transformers.py +89 -0
- django_cfg/apps/integrations/grpc/interceptors/__init__.py +3 -1
- django_cfg/apps/integrations/grpc/interceptors/centrifugo.py +541 -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/compile_proto.py +105 -0
- django_cfg/apps/integrations/grpc/management/commands/generate_protos.py +185 -0
- django_cfg/apps/integrations/grpc/management/commands/rungrpc.py +474 -95
- django_cfg/apps/integrations/grpc/management/commands/test_grpc_integration.py +75 -0
- django_cfg/apps/integrations/grpc/management/proto/__init__.py +3 -0
- django_cfg/apps/integrations/grpc/management/proto/compiler.py +194 -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/discovery.py +7 -1
- 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/SERVER_LOGGING.md +164 -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 +378 -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/serializers/config.py +95 -9
- django_cfg/apps/system/dashboard/serializers/statistics.py +9 -4
- django_cfg/apps/system/dashboard/services/commands_service.py +12 -1
- django_cfg/apps/system/frontend/views.py +87 -6
- 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/builders/security_builder.py +1 -0
- django_cfg/core/generation/integration_generators/api.py +2 -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/generator.py +26 -0
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +7 -1
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py +5 -0
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +11 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +1 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +29 -1
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/hooks.ts.jinja +4 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +12 -8
- django_cfg/modules/django_client/core/ir/schema.py +15 -1
- django_cfg/modules/django_client/core/parser/base.py +126 -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.20.dist-info}/METADATA +1 -1
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/RECORD +158 -121
- django_cfg/apps/integrations/grpc/auth/jwt_auth.py +0 -295
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/WHEEL +0 -0
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.5.8.dist-info → django_cfg-1.5.20.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gRPC API Key Model.
|
|
3
|
+
|
|
4
|
+
Django model for managing API keys used for gRPC authentication.
|
|
5
|
+
Provides secure, revocable authentication for services and CLI tools.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import secrets
|
|
9
|
+
from django.conf import settings
|
|
10
|
+
from django.db import models
|
|
11
|
+
from django.utils import timezone
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def generate_api_key() -> str:
|
|
15
|
+
"""
|
|
16
|
+
Generate a secure random API key.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
32-character hex string (128 bits of entropy)
|
|
20
|
+
"""
|
|
21
|
+
return secrets.token_hex(32)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class GrpcApiKey(models.Model):
|
|
25
|
+
"""
|
|
26
|
+
API Key for gRPC authentication.
|
|
27
|
+
|
|
28
|
+
Provides secure, revocable authentication for:
|
|
29
|
+
- Service-to-service communication
|
|
30
|
+
- CLI tools and scripts
|
|
31
|
+
- Internal systems
|
|
32
|
+
- Development and testing
|
|
33
|
+
|
|
34
|
+
Features:
|
|
35
|
+
- Auto-generated secure keys
|
|
36
|
+
- User association for permissions
|
|
37
|
+
- Expiration support
|
|
38
|
+
- Usage tracking
|
|
39
|
+
- Easy revocation via admin
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> # Create API key for a service
|
|
43
|
+
>>> key = GrpcApiKey.objects.create_for_user(
|
|
44
|
+
... user=admin_user,
|
|
45
|
+
... name="Analytics Service",
|
|
46
|
+
... description="Internal analytics microservice"
|
|
47
|
+
... )
|
|
48
|
+
>>> print(key.key) # Use this in service config
|
|
49
|
+
|
|
50
|
+
>>> # Check if key is valid
|
|
51
|
+
>>> if key.is_valid:
|
|
52
|
+
... user = key.user
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# Custom manager
|
|
56
|
+
from ..managers.grpc_api_key import GrpcApiKeyManager
|
|
57
|
+
objects: GrpcApiKeyManager = GrpcApiKeyManager()
|
|
58
|
+
|
|
59
|
+
class KeyTypeChoices(models.TextChoices):
|
|
60
|
+
"""Type of API key."""
|
|
61
|
+
SERVICE = "service", "Service-to-Service"
|
|
62
|
+
CLI = "cli", "CLI Tool"
|
|
63
|
+
WEBHOOK = "webhook", "Webhook"
|
|
64
|
+
INTERNAL = "internal", "Internal System"
|
|
65
|
+
DEVELOPMENT = "development", "Development"
|
|
66
|
+
|
|
67
|
+
# Identity
|
|
68
|
+
key = models.CharField(
|
|
69
|
+
max_length=64,
|
|
70
|
+
unique=True,
|
|
71
|
+
default=generate_api_key,
|
|
72
|
+
db_index=True,
|
|
73
|
+
help_text="API key (auto-generated)",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
name = models.CharField(
|
|
77
|
+
max_length=255,
|
|
78
|
+
help_text="Descriptive name for this key (e.g., 'Analytics Service')",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
description = models.TextField(
|
|
82
|
+
blank=True,
|
|
83
|
+
help_text="Additional details about this key's purpose",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
key_type = models.CharField(
|
|
87
|
+
max_length=20,
|
|
88
|
+
choices=KeyTypeChoices.choices,
|
|
89
|
+
default=KeyTypeChoices.SERVICE,
|
|
90
|
+
help_text="Type of API key",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# User association
|
|
94
|
+
user = models.ForeignKey(
|
|
95
|
+
settings.AUTH_USER_MODEL,
|
|
96
|
+
on_delete=models.CASCADE,
|
|
97
|
+
related_name="grpc_api_keys",
|
|
98
|
+
help_text="User this key authenticates as",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Status
|
|
102
|
+
is_active = models.BooleanField(
|
|
103
|
+
default=True,
|
|
104
|
+
db_index=True,
|
|
105
|
+
help_text="Whether this key is currently active (can be used)",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Expiration
|
|
109
|
+
expires_at = models.DateTimeField(
|
|
110
|
+
null=True,
|
|
111
|
+
blank=True,
|
|
112
|
+
db_index=True,
|
|
113
|
+
help_text="When this key expires (null = never expires)",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Usage tracking
|
|
117
|
+
last_used_at = models.DateTimeField(
|
|
118
|
+
null=True,
|
|
119
|
+
blank=True,
|
|
120
|
+
help_text="When this key was last used",
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
request_count = models.IntegerField(
|
|
124
|
+
default=0,
|
|
125
|
+
help_text="Total number of requests made with this key",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Metadata
|
|
129
|
+
created_by = models.ForeignKey(
|
|
130
|
+
settings.AUTH_USER_MODEL,
|
|
131
|
+
on_delete=models.SET_NULL,
|
|
132
|
+
null=True,
|
|
133
|
+
blank=True,
|
|
134
|
+
related_name="created_grpc_api_keys",
|
|
135
|
+
help_text="User who created this key",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Timestamps
|
|
139
|
+
created_at = models.DateTimeField(
|
|
140
|
+
auto_now_add=True,
|
|
141
|
+
db_index=True,
|
|
142
|
+
help_text="When this key was created",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
updated_at = models.DateTimeField(
|
|
146
|
+
auto_now=True,
|
|
147
|
+
help_text="When this key was last updated",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
class Meta:
|
|
151
|
+
db_table = "django_cfg_grpc_api_key"
|
|
152
|
+
ordering = ["-created_at"]
|
|
153
|
+
indexes = [
|
|
154
|
+
models.Index(fields=["user", "-created_at"]),
|
|
155
|
+
models.Index(fields=["is_active", "-created_at"]),
|
|
156
|
+
models.Index(fields=["expires_at"]),
|
|
157
|
+
models.Index(fields=["key_type", "-created_at"]),
|
|
158
|
+
]
|
|
159
|
+
verbose_name = "gRPC API Key"
|
|
160
|
+
verbose_name_plural = "gRPC API Keys"
|
|
161
|
+
|
|
162
|
+
def __str__(self) -> str:
|
|
163
|
+
"""String representation."""
|
|
164
|
+
status = "✓" if self.is_valid else "✗"
|
|
165
|
+
return f"{status} {self.name} ({self.user.username})"
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def is_expired(self) -> bool:
|
|
169
|
+
"""Check if key has expired."""
|
|
170
|
+
if not self.expires_at:
|
|
171
|
+
return False
|
|
172
|
+
return timezone.now() > self.expires_at
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def is_valid(self) -> bool:
|
|
176
|
+
"""Check if key is valid (active and not expired)."""
|
|
177
|
+
return self.is_active and not self.is_expired
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def masked_key(self) -> str:
|
|
181
|
+
"""Return masked version of key for display."""
|
|
182
|
+
if len(self.key) <= 8:
|
|
183
|
+
return self.key
|
|
184
|
+
return f"{self.key[:4]}...{self.key[-4:]}"
|
|
185
|
+
|
|
186
|
+
def mark_used(self) -> None:
|
|
187
|
+
"""Mark this key as used (update last_used_at and increment counter)."""
|
|
188
|
+
self.last_used_at = timezone.now()
|
|
189
|
+
self.request_count += 1
|
|
190
|
+
self.save(update_fields=["last_used_at", "request_count"])
|
|
191
|
+
|
|
192
|
+
def revoke(self) -> None:
|
|
193
|
+
"""Revoke this key (set is_active=False)."""
|
|
194
|
+
self.is_active = False
|
|
195
|
+
self.save(update_fields=["is_active"])
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
__all__ = ["GrpcApiKey", "generate_api_key"]
|
|
@@ -9,6 +9,7 @@ from django.conf import settings
|
|
|
9
9
|
from django.db import models
|
|
10
10
|
from django.utils import timezone
|
|
11
11
|
|
|
12
|
+
from .grpc_api_key import GrpcApiKey
|
|
12
13
|
|
|
13
14
|
class GRPCRequestLog(models.Model):
|
|
14
15
|
"""
|
|
@@ -138,6 +139,15 @@ class GRPCRequestLog(models.Model):
|
|
|
138
139
|
help_text="Authenticated user (if applicable)",
|
|
139
140
|
)
|
|
140
141
|
|
|
142
|
+
api_key = models.ForeignKey(
|
|
143
|
+
GrpcApiKey,
|
|
144
|
+
on_delete=models.SET_NULL,
|
|
145
|
+
null=True,
|
|
146
|
+
blank=True,
|
|
147
|
+
related_name="request_logs",
|
|
148
|
+
help_text="API key used for authentication (if applicable)",
|
|
149
|
+
)
|
|
150
|
+
|
|
141
151
|
is_authenticated = models.BooleanField(
|
|
142
152
|
default=False,
|
|
143
153
|
db_index=True,
|
|
@@ -186,6 +196,7 @@ class GRPCRequestLog(models.Model):
|
|
|
186
196
|
models.Index(fields=["method_name", "-created_at"]),
|
|
187
197
|
models.Index(fields=["status", "-created_at"]),
|
|
188
198
|
models.Index(fields=["user", "-created_at"]),
|
|
199
|
+
models.Index(fields=["api_key", "-created_at"]),
|
|
189
200
|
models.Index(fields=["grpc_status_code", "-created_at"]),
|
|
190
201
|
]
|
|
191
202
|
verbose_name = "gRPC Request Log"
|
|
@@ -174,11 +174,17 @@ class GRPCServerStatus(models.Model):
|
|
|
174
174
|
|
|
175
175
|
@property
|
|
176
176
|
def is_running(self) -> bool:
|
|
177
|
-
"""
|
|
177
|
+
"""
|
|
178
|
+
Check if server is currently running.
|
|
179
|
+
|
|
180
|
+
Uses environment-aware detection:
|
|
181
|
+
- Production: Assumes external server (Docker), relies on heartbeat only
|
|
182
|
+
- Development/Test: Checks local process + heartbeat
|
|
183
|
+
"""
|
|
178
184
|
if self.status not in [self.StatusChoices.RUNNING, self.StatusChoices.STARTING]:
|
|
179
185
|
return False
|
|
180
186
|
|
|
181
|
-
# Check if process is still alive
|
|
187
|
+
# Check if process is still alive (auto-detects external servers)
|
|
182
188
|
if not self._is_process_alive():
|
|
183
189
|
return False
|
|
184
190
|
|
|
@@ -227,12 +233,41 @@ class GRPCServerStatus(models.Model):
|
|
|
227
233
|
return " ".join(parts)
|
|
228
234
|
|
|
229
235
|
def _is_process_alive(self) -> bool:
|
|
230
|
-
"""
|
|
236
|
+
"""
|
|
237
|
+
Check if the process is still running.
|
|
238
|
+
|
|
239
|
+
Uses environment-aware detection:
|
|
240
|
+
- Production mode: Skip PID check (assumes external server in Docker)
|
|
241
|
+
- Development/Test: Check PID with graceful fallback for containers
|
|
242
|
+
"""
|
|
243
|
+
try:
|
|
244
|
+
from django_cfg.core import get_current_config
|
|
245
|
+
|
|
246
|
+
# Auto-detect based on env_mode
|
|
247
|
+
config = get_current_config()
|
|
248
|
+
if config and str(config.env_mode) == "production":
|
|
249
|
+
# Production = external server in separate container
|
|
250
|
+
# Don't check PID, rely on heartbeat only
|
|
251
|
+
return True
|
|
252
|
+
|
|
253
|
+
except Exception:
|
|
254
|
+
# Config not available, use fallback logic
|
|
255
|
+
pass
|
|
256
|
+
|
|
257
|
+
# Development/Test or fallback: check PID with graceful handling
|
|
231
258
|
try:
|
|
232
259
|
# Send signal 0 to check if process exists
|
|
233
260
|
os.kill(self.pid, 0)
|
|
234
261
|
return True
|
|
235
|
-
except
|
|
262
|
+
except ProcessLookupError:
|
|
263
|
+
# PID not found - could be different namespace (Docker)
|
|
264
|
+
# Don't mark as dead immediately, rely on heartbeat
|
|
265
|
+
return True
|
|
266
|
+
except PermissionError:
|
|
267
|
+
# Process exists but no permission to signal
|
|
268
|
+
return True
|
|
269
|
+
except OSError:
|
|
270
|
+
# Other OS error (e.g., process died)
|
|
236
271
|
return False
|
|
237
272
|
|
|
238
273
|
def mark_running(self):
|
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
Pydantic serializers for gRPC monitoring API.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from .api_keys import (
|
|
6
|
+
ApiKeyListSerializer,
|
|
7
|
+
ApiKeySerializer,
|
|
8
|
+
ApiKeyStatsSerializer,
|
|
9
|
+
)
|
|
10
|
+
from .proto_files import (
|
|
11
|
+
ProtoFileDetailSerializer,
|
|
12
|
+
ProtoFileListSerializer,
|
|
13
|
+
ProtoGenerateRequestSerializer,
|
|
14
|
+
ProtoGenerateResponseSerializer,
|
|
15
|
+
)
|
|
5
16
|
from .charts import (
|
|
6
17
|
DashboardChartsSerializer,
|
|
7
18
|
ErrorDistributionChartSerializer,
|
|
@@ -17,14 +28,12 @@ from .requests import RecentRequestsSerializer
|
|
|
17
28
|
from .service_registry import (
|
|
18
29
|
MethodDetailSerializer,
|
|
19
30
|
ServiceDetailSerializer,
|
|
20
|
-
ServiceListSerializer
|
|
31
|
+
ServiceListSerializer,
|
|
21
32
|
ServiceMethodsSerializer,
|
|
22
33
|
)
|
|
23
34
|
from .services import (
|
|
24
35
|
MethodListSerializer,
|
|
25
36
|
MethodStatsSerializer,
|
|
26
|
-
MonitoringServiceStatsSerializer,
|
|
27
|
-
ServiceListSerializer,
|
|
28
37
|
)
|
|
29
38
|
from .stats import GRPCOverviewStatsSerializer
|
|
30
39
|
from .testing import (
|
|
@@ -39,15 +48,13 @@ __all__ = [
|
|
|
39
48
|
"GRPCHealthCheckSerializer",
|
|
40
49
|
"GRPCOverviewStatsSerializer",
|
|
41
50
|
"RecentRequestsSerializer",
|
|
42
|
-
"MonitoringServiceStatsSerializer",
|
|
43
|
-
"ServiceListSerializer",
|
|
44
51
|
"MethodStatsSerializer",
|
|
45
52
|
"MethodListSerializer",
|
|
46
53
|
# Config
|
|
47
54
|
"GRPCConfigSerializer",
|
|
48
55
|
"GRPCServerInfoSerializer",
|
|
49
56
|
# Service Registry
|
|
50
|
-
"
|
|
57
|
+
"ServiceListSerializer",
|
|
51
58
|
"ServiceDetailSerializer",
|
|
52
59
|
"ServiceMethodsSerializer",
|
|
53
60
|
"MethodDetailSerializer",
|
|
@@ -64,4 +71,13 @@ __all__ = [
|
|
|
64
71
|
"ServerLifecycleChartSerializer",
|
|
65
72
|
"ErrorDistributionChartSerializer",
|
|
66
73
|
"DashboardChartsSerializer",
|
|
74
|
+
# API Keys
|
|
75
|
+
"ApiKeySerializer",
|
|
76
|
+
"ApiKeyListSerializer",
|
|
77
|
+
"ApiKeyStatsSerializer",
|
|
78
|
+
# Proto Files
|
|
79
|
+
"ProtoFileDetailSerializer",
|
|
80
|
+
"ProtoFileListSerializer",
|
|
81
|
+
"ProtoGenerateRequestSerializer",
|
|
82
|
+
"ProtoGenerateResponseSerializer",
|
|
67
83
|
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DRF serializers for gRPC API Keys.
|
|
3
|
+
|
|
4
|
+
Read-only serializers for listing and viewing API keys.
|
|
5
|
+
Create/Update/Delete operations handled through Django Admin.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from rest_framework import serializers
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ApiKeySerializer(serializers.Serializer):
|
|
12
|
+
"""API Key information (read-only)."""
|
|
13
|
+
|
|
14
|
+
id = serializers.IntegerField(help_text="Database ID")
|
|
15
|
+
name = serializers.CharField(help_text="Key name/description")
|
|
16
|
+
key_type = serializers.CharField(help_text="Type of API key")
|
|
17
|
+
masked_key = serializers.CharField(help_text="Masked API key (first 4 and last 4 chars)")
|
|
18
|
+
is_active = serializers.BooleanField(help_text="Whether key is active")
|
|
19
|
+
is_valid = serializers.BooleanField(help_text="Whether key is valid (active and not expired)")
|
|
20
|
+
user_id = serializers.IntegerField(help_text="User ID")
|
|
21
|
+
username = serializers.CharField(help_text="Username")
|
|
22
|
+
user_email = serializers.CharField(help_text="User email", allow_blank=True)
|
|
23
|
+
request_count = serializers.IntegerField(help_text="Total requests made with this key")
|
|
24
|
+
last_used_at = serializers.DateTimeField(
|
|
25
|
+
allow_null=True,
|
|
26
|
+
help_text="When key was last used"
|
|
27
|
+
)
|
|
28
|
+
expires_at = serializers.DateTimeField(
|
|
29
|
+
allow_null=True,
|
|
30
|
+
help_text="When key expires (null = never)"
|
|
31
|
+
)
|
|
32
|
+
created_at = serializers.DateTimeField(help_text="When key was created")
|
|
33
|
+
created_by = serializers.CharField(
|
|
34
|
+
allow_null=True,
|
|
35
|
+
help_text="User who created this key"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ApiKeyListSerializer(serializers.Serializer):
|
|
40
|
+
"""List of API keys response."""
|
|
41
|
+
|
|
42
|
+
results = ApiKeySerializer(many=True, help_text="List of API keys")
|
|
43
|
+
count = serializers.IntegerField(help_text="Total number of API keys")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ApiKeyStatsSerializer(serializers.Serializer):
|
|
47
|
+
"""API Key usage statistics."""
|
|
48
|
+
|
|
49
|
+
total_keys = serializers.IntegerField(help_text="Total API keys")
|
|
50
|
+
active_keys = serializers.IntegerField(help_text="Active API keys")
|
|
51
|
+
expired_keys = serializers.IntegerField(help_text="Expired API keys")
|
|
52
|
+
total_requests = serializers.IntegerField(help_text="Total requests across all keys")
|
|
53
|
+
keys_by_type = serializers.DictField(
|
|
54
|
+
child=serializers.IntegerField(),
|
|
55
|
+
help_text="Count of keys by type"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
"ApiKeySerializer",
|
|
61
|
+
"ApiKeyListSerializer",
|
|
62
|
+
"ApiKeyStatsSerializer",
|
|
63
|
+
]
|