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
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { APIClient } from "./client";
|
|
36
|
-
import { OPENAPI_SCHEMA } from "./schema";
|
|
37
36
|
import {
|
|
38
37
|
StorageAdapter,
|
|
39
38
|
LocalStorageAdapter,
|
|
@@ -82,9 +81,6 @@ export * as Hooks from "./_utils/hooks";
|
|
|
82
81
|
// Re-export core client
|
|
83
82
|
export { APIClient };
|
|
84
83
|
|
|
85
|
-
// Re-export OpenAPI schema
|
|
86
|
-
export { OPENAPI_SCHEMA };
|
|
87
|
-
|
|
88
84
|
// Re-export storage adapters for convenience
|
|
89
85
|
export type { StorageAdapter };
|
|
90
86
|
export { LocalStorageAdapter, CookieStorageAdapter, MemoryStorageAdapter };
|
|
@@ -267,11 +263,19 @@ export class API {
|
|
|
267
263
|
}
|
|
268
264
|
|
|
269
265
|
/**
|
|
270
|
-
* Get OpenAPI schema
|
|
271
|
-
* @returns
|
|
266
|
+
* Get OpenAPI schema path
|
|
267
|
+
* @returns Path to the OpenAPI schema JSON file
|
|
268
|
+
*
|
|
269
|
+
* Note: The OpenAPI schema is available in the schema.json file.
|
|
270
|
+
* You can load it dynamically using:
|
|
271
|
+
* ```typescript
|
|
272
|
+
* const schema = await fetch('./schema.json').then(r => r.json());
|
|
273
|
+
* // or using fs in Node.js:
|
|
274
|
+
* // const schema = JSON.parse(fs.readFileSync('./schema.json', 'utf-8'));
|
|
275
|
+
* ```
|
|
272
276
|
*/
|
|
273
|
-
|
|
274
|
-
return
|
|
277
|
+
getSchemaPath(): string {
|
|
278
|
+
return './schema.json';
|
|
275
279
|
}
|
|
276
280
|
}
|
|
277
281
|
|
|
@@ -62,8 +62,12 @@ class BaseParser(ABC):
|
|
|
62
62
|
IRContext with all schemas and operations
|
|
63
63
|
|
|
64
64
|
Raises:
|
|
65
|
-
ValueError: If COMPONENT_SPLIT_REQUEST is not detected
|
|
65
|
+
ValueError: If COMPONENT_SPLIT_REQUEST is not detected or schema name conflicts found
|
|
66
66
|
"""
|
|
67
|
+
# CRITICAL: Validate schema names BEFORE parsing
|
|
68
|
+
# This prevents generation from starting with invalid schemas
|
|
69
|
+
self._validate_schema_names()
|
|
70
|
+
|
|
67
71
|
# Parse metadata
|
|
68
72
|
openapi_info = self._parse_openapi_info()
|
|
69
73
|
django_metadata = self._parse_django_metadata()
|
|
@@ -237,54 +241,134 @@ class BaseParser(ABC):
|
|
|
237
241
|
|
|
238
242
|
return False
|
|
239
243
|
|
|
240
|
-
# ===== Schema
|
|
244
|
+
# ===== Schema Validation =====
|
|
245
|
+
|
|
246
|
+
def _validate_schema_names(self) -> None:
|
|
247
|
+
"""
|
|
248
|
+
Validate schema names for conflicts BEFORE parsing.
|
|
249
|
+
|
|
250
|
+
This method checks for:
|
|
251
|
+
1. Case-insensitive duplicates (e.g., "User" and "user")
|
|
252
|
+
2. Exact duplicates (e.g., "GRPCServerInfo" from two different serializers)
|
|
253
|
+
|
|
254
|
+
Raises:
|
|
255
|
+
ValueError: If schema name conflicts are detected (hard exit with traceback)
|
|
256
|
+
|
|
257
|
+
Example conflicts:
|
|
258
|
+
- Case-insensitive: "Profile" and "profile" → filesystem conflict
|
|
259
|
+
- Exact duplicate: "HealthCheck" from multiple serializers → schema conflict
|
|
260
|
+
"""
|
|
261
|
+
import traceback
|
|
262
|
+
import sys
|
|
241
263
|
|
|
242
|
-
def _parse_all_schemas(self) -> dict[str, IRSchemaObject]:
|
|
243
|
-
"""Parse all schemas from components."""
|
|
244
264
|
if not self.spec.components or not self.spec.components.schemas:
|
|
245
|
-
return
|
|
265
|
+
return # No schemas to validate
|
|
246
266
|
|
|
247
|
-
# Check for duplicate schema names with different casing
|
|
248
267
|
schema_names = list(self.spec.components.schemas.keys())
|
|
249
268
|
lowercase_map = {}
|
|
250
|
-
|
|
269
|
+
exact_duplicate_sources = {}
|
|
251
270
|
|
|
252
271
|
for name in schema_names:
|
|
253
272
|
lowercase = name.lower()
|
|
254
273
|
|
|
255
274
|
# Check case-insensitive duplicates
|
|
256
275
|
if lowercase in lowercase_map:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
f"
|
|
260
|
-
f"
|
|
261
|
-
f"
|
|
262
|
-
f"
|
|
276
|
+
existing_name = lowercase_map[lowercase]
|
|
277
|
+
error_msg = (
|
|
278
|
+
f"\n{'=' * 80}\n"
|
|
279
|
+
f"❌ SCHEMA NAME CONFLICT DETECTED\n"
|
|
280
|
+
f"{'=' * 80}\n\n"
|
|
281
|
+
f"Conflict Type: Case-insensitive duplicate\n"
|
|
282
|
+
f"Schema Names:\n"
|
|
283
|
+
f" 1. '{existing_name}'\n"
|
|
284
|
+
f" 2. '{name}'\n\n"
|
|
285
|
+
f"Problem:\n"
|
|
286
|
+
f" These names differ only in casing and will cause conflicts on\n"
|
|
287
|
+
f" case-insensitive filesystems (macOS, Windows).\n\n"
|
|
288
|
+
f"Solution:\n"
|
|
289
|
+
f" Rename one of the Django serializers to make them distinct.\n"
|
|
290
|
+
f" Example: {name}Serializer → {name}DetailSerializer\n\n"
|
|
291
|
+
f"{'=' * 80}\n"
|
|
263
292
|
)
|
|
264
|
-
lowercase_map[lowercase] = name
|
|
265
293
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
title1 = getattr(schema1, 'title', 'Unknown')
|
|
271
|
-
title2 = exact_duplicate_map[name]
|
|
294
|
+
# Print full error with traceback
|
|
295
|
+
print(error_msg, file=sys.stderr)
|
|
296
|
+
print("\n🔍 Traceback (schema validation):", file=sys.stderr)
|
|
297
|
+
traceback.print_stack(file=sys.stderr)
|
|
272
298
|
|
|
299
|
+
# Hard exit - stop generation immediately
|
|
273
300
|
raise ValueError(
|
|
274
|
-
f"
|
|
275
|
-
f"
|
|
276
|
-
f" - {title1}\n"
|
|
277
|
-
f" - {title2}\n"
|
|
278
|
-
f"This causes schema conflicts in the generated API client.\n"
|
|
279
|
-
f"Please rename one of the serializers to make them unique.\n"
|
|
280
|
-
f"Example: HealthCheckSerializer → GRPCHealthCheckSerializer"
|
|
301
|
+
f"Case-insensitive schema name conflict: '{existing_name}' vs '{name}'. "
|
|
302
|
+
f"Cannot generate client with conflicting schema names."
|
|
281
303
|
)
|
|
282
304
|
|
|
283
|
-
|
|
305
|
+
lowercase_map[lowercase] = name
|
|
306
|
+
|
|
307
|
+
# Track schema sources for exact duplicate detection
|
|
284
308
|
schema = self.spec.components.schemas.get(name)
|
|
285
|
-
|
|
286
|
-
|
|
309
|
+
if schema and not isinstance(schema, ReferenceObject):
|
|
310
|
+
# Get source information from schema
|
|
311
|
+
title = getattr(schema, 'title', name)
|
|
312
|
+
description = getattr(schema, 'description', '')
|
|
313
|
+
|
|
314
|
+
# Create signature for duplicate detection
|
|
315
|
+
if name in exact_duplicate_sources:
|
|
316
|
+
# Found exact duplicate!
|
|
317
|
+
existing = exact_duplicate_sources[name]
|
|
318
|
+
error_msg = (
|
|
319
|
+
f"\n{'=' * 80}\n"
|
|
320
|
+
f"❌ SCHEMA NAME CONFLICT DETECTED\n"
|
|
321
|
+
f"{'=' * 80}\n\n"
|
|
322
|
+
f"Conflict Type: Exact duplicate schema name\n"
|
|
323
|
+
f"Schema Name: '{name}'\n\n"
|
|
324
|
+
f"Sources:\n"
|
|
325
|
+
f" 1. {existing['title']}\n"
|
|
326
|
+
f" Description: {existing['description'][:100]}\n"
|
|
327
|
+
f" 2. {title}\n"
|
|
328
|
+
f" Description: {description[:100]}\n\n"
|
|
329
|
+
f"Problem:\n"
|
|
330
|
+
f" Multiple serializers are generating the same schema name '{name}'.\n"
|
|
331
|
+
f" This causes schema conflicts in the generated API client and\n"
|
|
332
|
+
f" OpenAPI documentation.\n\n"
|
|
333
|
+
f"Solution:\n"
|
|
334
|
+
f" Rename one of the serializers to make them unique.\n"
|
|
335
|
+
f" Examples:\n"
|
|
336
|
+
f" - HealthCheckSerializer → GRPCHealthCheckSerializer\n"
|
|
337
|
+
f" - ServerInfoSerializer → GRPCServerStatusSerializer\n\n"
|
|
338
|
+
f"{'=' * 80}\n"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Print full error with traceback
|
|
342
|
+
print(error_msg, file=sys.stderr)
|
|
343
|
+
print("\n🔍 Traceback (schema validation):", file=sys.stderr)
|
|
344
|
+
traceback.print_stack(file=sys.stderr)
|
|
345
|
+
|
|
346
|
+
# Hard exit - stop generation immediately
|
|
347
|
+
raise ValueError(
|
|
348
|
+
f"Duplicate schema name detected: '{name}'. "
|
|
349
|
+
f"Multiple serializers are generating this schema. "
|
|
350
|
+
f"Cannot generate client with conflicting schemas."
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Store for duplicate detection
|
|
354
|
+
exact_duplicate_sources[name] = {
|
|
355
|
+
'title': title,
|
|
356
|
+
'description': description,
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
# ===== Schema Parsing =====
|
|
360
|
+
|
|
361
|
+
def _parse_all_schemas(self) -> dict[str, IRSchemaObject]:
|
|
362
|
+
"""
|
|
363
|
+
Parse all schemas from components.
|
|
364
|
+
|
|
365
|
+
Note: Schema name validation is done in _validate_schema_names()
|
|
366
|
+
which is called BEFORE this method in parse().
|
|
367
|
+
"""
|
|
368
|
+
if not self.spec.components or not self.spec.components.schemas:
|
|
369
|
+
return {}
|
|
287
370
|
|
|
371
|
+
# Parse schemas (validation already done in _validate_schema_names)
|
|
288
372
|
schemas = {}
|
|
289
373
|
for name, schema_or_ref in self.spec.components.schemas.items():
|
|
290
374
|
# Skip references for now
|
|
@@ -8,12 +8,15 @@ Usage:
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
from django.core.management.base import
|
|
11
|
+
from django.core.management.base import CommandError
|
|
12
12
|
|
|
13
|
+
from django_cfg.management.utils import AdminCommand
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
class Command(AdminCommand):
|
|
15
17
|
"""Generate OpenAPI clients for configured application groups."""
|
|
16
18
|
|
|
19
|
+
command_name = 'generate_client'
|
|
17
20
|
help = "Generate Python, TypeScript, and Go API clients from OpenAPI schemas"
|
|
18
21
|
|
|
19
22
|
def add_arguments(self, parser):
|
|
@@ -12,12 +12,15 @@ Usage:
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import List
|
|
14
14
|
|
|
15
|
-
from django.core.management.base import
|
|
15
|
+
from django.core.management.base import CommandError
|
|
16
16
|
|
|
17
|
+
from django_cfg.management.utils import AdminCommand
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
class Command(AdminCommand):
|
|
19
21
|
"""Validate and fix OpenAPI schema quality issues in DRF serializers."""
|
|
20
22
|
|
|
23
|
+
command_name = 'validate_openapi'
|
|
21
24
|
help = "Validate and auto-fix OpenAPI schema quality issues"
|
|
22
25
|
|
|
23
26
|
def add_arguments(self, parser):
|
|
@@ -5,22 +5,16 @@ Tests email sending functionality using django_cfg configuration.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from django.contrib.auth import get_user_model
|
|
8
|
-
from django.core.management.base import BaseCommand
|
|
9
8
|
|
|
10
|
-
from django_cfg.
|
|
9
|
+
from django_cfg.management.utils import SafeCommand
|
|
11
10
|
|
|
12
11
|
User = get_user_model()
|
|
13
|
-
logger = get_logger('test_email')
|
|
14
12
|
|
|
15
13
|
|
|
16
|
-
class Command(
|
|
14
|
+
class Command(SafeCommand):
|
|
17
15
|
"""Command to test email functionality."""
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
web_executable = True
|
|
21
|
-
requires_input = False
|
|
22
|
-
is_destructive = False
|
|
23
|
-
|
|
17
|
+
command_name = 'test_email'
|
|
24
18
|
help = "Test email sending functionality"
|
|
25
19
|
|
|
26
20
|
def add_arguments(self, parser):
|
|
@@ -48,7 +42,7 @@ class Command(BaseCommand):
|
|
|
48
42
|
subject = options["subject"]
|
|
49
43
|
message = options["message"]
|
|
50
44
|
|
|
51
|
-
logger.info(f"Starting email test for {email}")
|
|
45
|
+
self.logger.info(f"Starting email test for {email}")
|
|
52
46
|
self.stdout.write(f"🚀 Testing email service for {email}")
|
|
53
47
|
|
|
54
48
|
# Create test user if not exists
|
|
@@ -12,8 +12,6 @@ from django.core.management.commands.runserver import Command as RunServerComman
|
|
|
12
12
|
from django_cfg.modules.django_logging import get_logger
|
|
13
13
|
from django_cfg.modules.django_ngrok import get_ngrok_service
|
|
14
14
|
|
|
15
|
-
logger = get_logger('runserver_ngrok')
|
|
16
|
-
|
|
17
15
|
|
|
18
16
|
class Command(RunServerCommand):
|
|
19
17
|
"""Enhanced runserver command with ngrok tunnel support."""
|
|
@@ -25,6 +23,11 @@ class Command(RunServerCommand):
|
|
|
25
23
|
|
|
26
24
|
help = f'{RunServerCommand.help.rstrip(".")} with ngrok tunnel.'
|
|
27
25
|
|
|
26
|
+
def __init__(self, *args, **kwargs):
|
|
27
|
+
"""Initialize with logger."""
|
|
28
|
+
super().__init__(*args, **kwargs)
|
|
29
|
+
self.logger = get_logger('runserver_ngrok')
|
|
30
|
+
|
|
28
31
|
def add_arguments(self, parser):
|
|
29
32
|
super().add_arguments(parser)
|
|
30
33
|
parser.add_argument(
|
|
@@ -76,14 +79,14 @@ class Command(RunServerCommand):
|
|
|
76
79
|
ngrok_service = get_ngrok_service()
|
|
77
80
|
|
|
78
81
|
self.stdout.write("🚇 Starting ngrok tunnel...")
|
|
79
|
-
logger.info(f"Starting ngrok tunnel for port {server_port}")
|
|
82
|
+
self.logger.info(f"Starting ngrok tunnel for port {server_port}")
|
|
80
83
|
|
|
81
84
|
tunnel_url = ngrok_service.start_tunnel(server_port)
|
|
82
85
|
|
|
83
86
|
if tunnel_url:
|
|
84
87
|
# Wait for tunnel to be fully established
|
|
85
88
|
self.stdout.write("⏳ Waiting for tunnel to be established...")
|
|
86
|
-
logger.info("Waiting for ngrok tunnel to be fully established")
|
|
89
|
+
self.logger.info("Waiting for ngrok tunnel to be fully established")
|
|
87
90
|
|
|
88
91
|
max_retries = 10
|
|
89
92
|
retry_count = 0
|
|
@@ -98,10 +101,10 @@ class Command(RunServerCommand):
|
|
|
98
101
|
current_url = ngrok_service.get_tunnel_url()
|
|
99
102
|
if current_url and current_url == tunnel_url:
|
|
100
103
|
tunnel_ready = True
|
|
101
|
-
logger.info(f"Ngrok tunnel established successfully: {tunnel_url}")
|
|
104
|
+
self.logger.info(f"Ngrok tunnel established successfully: {tunnel_url}")
|
|
102
105
|
break
|
|
103
106
|
except Exception as e:
|
|
104
|
-
logger.warning(f"Tunnel check attempt {retry_count} failed: {e}")
|
|
107
|
+
self.logger.warning(f"Tunnel check attempt {retry_count} failed: {e}")
|
|
105
108
|
|
|
106
109
|
self.stdout.write(f"⏳ Tunnel check {retry_count}/{max_retries}...")
|
|
107
110
|
|
|
@@ -116,16 +119,16 @@ class Command(RunServerCommand):
|
|
|
116
119
|
self.stdout.write(
|
|
117
120
|
self.style.SUCCESS(f"✅ Ngrok tunnel ready: {tunnel_url}")
|
|
118
121
|
)
|
|
119
|
-
logger.info(f"Ngrok tunnel fully ready: {tunnel_url}")
|
|
122
|
+
self.logger.info(f"Ngrok tunnel fully ready: {tunnel_url}")
|
|
120
123
|
else:
|
|
121
124
|
self.stdout.write(
|
|
122
125
|
self.style.WARNING("⚠️ Ngrok tunnel started but may not be fully ready")
|
|
123
126
|
)
|
|
124
|
-
logger.warning("Ngrok tunnel started but readiness check failed")
|
|
127
|
+
self.logger.warning("Ngrok tunnel started but readiness check failed")
|
|
125
128
|
else:
|
|
126
129
|
error_msg = "Failed to start ngrok tunnel"
|
|
127
130
|
self.stdout.write(self.style.ERROR(f"❌ {error_msg}"))
|
|
128
|
-
logger.error(error_msg)
|
|
131
|
+
self.logger.error(error_msg)
|
|
129
132
|
|
|
130
133
|
def _set_ngrok_env_vars(self, tunnel_url: str):
|
|
131
134
|
"""Set environment variables with ngrok URL for easy access."""
|
|
@@ -145,10 +148,10 @@ class Command(RunServerCommand):
|
|
|
145
148
|
os.environ['NGROK_API_URL'] = tunnel_url
|
|
146
149
|
|
|
147
150
|
# Environment variables set - no need for verbose output
|
|
148
|
-
logger.info(f"Set ngrok environment variables: {tunnel_url}")
|
|
151
|
+
self.logger.info(f"Set ngrok environment variables: {tunnel_url}")
|
|
149
152
|
|
|
150
153
|
except Exception as e:
|
|
151
|
-
logger.warning(f"Could not set ngrok environment variables: {e}")
|
|
154
|
+
self.logger.warning(f"Could not set ngrok environment variables: {e}")
|
|
152
155
|
|
|
153
156
|
def _update_allowed_hosts(self, tunnel_url: str):
|
|
154
157
|
"""Update ALLOWED_HOSTS with ngrok domain."""
|
|
@@ -164,7 +167,7 @@ class Command(RunServerCommand):
|
|
|
164
167
|
if hasattr(settings, 'ALLOWED_HOSTS'):
|
|
165
168
|
if ngrok_host not in settings.ALLOWED_HOSTS:
|
|
166
169
|
settings.ALLOWED_HOSTS.append(ngrok_host)
|
|
167
|
-
logger.info(f"Added {ngrok_host} to ALLOWED_HOSTS")
|
|
170
|
+
self.logger.info(f"Added {ngrok_host} to ALLOWED_HOSTS")
|
|
168
171
|
|
|
169
172
|
except Exception as e:
|
|
170
|
-
logger.warning(f"Could not update ALLOWED_HOSTS: {e}")
|
|
173
|
+
self.logger.warning(f"Could not update ALLOWED_HOSTS: {e}")
|
|
@@ -4,20 +4,13 @@ Test Telegram Command
|
|
|
4
4
|
Tests Telegram notification functionality using django_cfg configuration.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from django_cfg.management.utils import SafeCommand
|
|
8
8
|
|
|
9
|
-
from django_cfg.modules.django_logging import get_logger
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Command(BaseCommand):
|
|
10
|
+
class Command(SafeCommand):
|
|
14
11
|
"""Command to test Telegram functionality."""
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
web_executable = True
|
|
18
|
-
requires_input = False
|
|
19
|
-
is_destructive = False
|
|
20
|
-
|
|
13
|
+
command_name = 'test_telegram'
|
|
21
14
|
help = "Test Telegram notification functionality"
|
|
22
15
|
|
|
23
16
|
def add_arguments(self, parser):
|
|
@@ -29,7 +22,7 @@ class Command(BaseCommand):
|
|
|
29
22
|
)
|
|
30
23
|
|
|
31
24
|
def handle(self, *args, **options):
|
|
32
|
-
logger.info("Starting test_telegram command")
|
|
25
|
+
self.logger.info("Starting test_telegram command")
|
|
33
26
|
message = options["message"]
|
|
34
27
|
|
|
35
28
|
self.stdout.write("🚀 Testing Telegram notification service")
|
|
@@ -4,20 +4,13 @@ Test Twilio Command
|
|
|
4
4
|
Tests Twilio messaging functionality using django_cfg configuration.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from django_cfg.management.utils import SafeCommand
|
|
8
8
|
|
|
9
|
-
from django_cfg.modules.django_logging import get_logger
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class Command(BaseCommand):
|
|
10
|
+
class Command(SafeCommand):
|
|
14
11
|
"""Command to test Twilio functionality."""
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
web_executable = True
|
|
18
|
-
requires_input = False
|
|
19
|
-
is_destructive = False
|
|
20
|
-
|
|
13
|
+
command_name = 'test_twilio'
|
|
21
14
|
help = "Test Twilio messaging functionality"
|
|
22
15
|
|
|
23
16
|
def add_arguments(self, parser):
|
|
@@ -45,7 +38,7 @@ class Command(BaseCommand):
|
|
|
45
38
|
)
|
|
46
39
|
|
|
47
40
|
def handle(self, *args, **options):
|
|
48
|
-
logger.info("Starting test_twilio command")
|
|
41
|
+
self.logger.info("Starting test_twilio command")
|
|
49
42
|
to_number = options["to"]
|
|
50
43
|
message = options["message"]
|
|
51
44
|
is_whatsapp = options["whatsapp"]
|
|
@@ -148,29 +148,17 @@ class NavigationManager(BaseCfgModule):
|
|
|
148
148
|
|
|
149
149
|
# gRPC Dashboard (if enabled)
|
|
150
150
|
if self.is_grpc_enabled():
|
|
151
|
-
grpc_items = []
|
|
152
|
-
|
|
153
|
-
# Monitoring API endpoint
|
|
154
|
-
grpc_items.append(
|
|
155
|
-
NavigationItem(title="Monitor", icon=Icons.MONITOR_HEART, link="/cfg/admin/admin/dashboard/grpc/")
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# Request Logs with safe URL resolution
|
|
159
|
-
logs_item = self._create_nav_item(
|
|
160
|
-
title="Request Logs",
|
|
161
|
-
icon=Icons.LIST_ALT,
|
|
162
|
-
url_name="admin:grpc_grpcrequestlog_changelist",
|
|
163
|
-
fallback_link="/admin/grpc/grpcrequestlog/"
|
|
164
|
-
)
|
|
165
|
-
if logs_item:
|
|
166
|
-
grpc_items.append(logs_item)
|
|
167
|
-
|
|
168
151
|
navigation_sections.append(
|
|
169
152
|
NavigationSection(
|
|
170
153
|
title="gRPC",
|
|
171
154
|
separator=True,
|
|
172
155
|
collapsible=True,
|
|
173
|
-
items=
|
|
156
|
+
items=[
|
|
157
|
+
NavigationItem(title="Monitor", icon=Icons.MONITOR_HEART, link="/cfg/admin/admin/dashboard/grpc/"),
|
|
158
|
+
NavigationItem(title="Request Logs", icon=Icons.LIST_ALT, link=str(reverse_lazy("admin:grpc_grpcrequestlog_changelist"))),
|
|
159
|
+
NavigationItem(title="API Keys", icon=Icons.KEY, link=str(reverse_lazy("admin:grpc_grpcapikey_changelist"))),
|
|
160
|
+
NavigationItem(title="Server Status", icon=Icons.HEALTH_AND_SAFETY, link=str(reverse_lazy("admin:grpc_grpcserverstatus_changelist"))),
|
|
161
|
+
]
|
|
174
162
|
)
|
|
175
163
|
)
|
|
176
164
|
|
django_cfg/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "django-cfg"
|
|
7
|
-
version = "1.5.
|
|
7
|
+
version = "1.5.14"
|
|
8
8
|
description = "Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [ "django", "configuration", "pydantic", "settings", "type-safety", "pydantic-settings", "django-environ", "startup-validation", "ide-autocomplete", "nextjs-admin", "react-admin", "websocket", "centrifugo", "real-time", "typescript-generation", "ai-agents", "enterprise-django", "django-settings", "type-safe-config", "modern-django",]
|
django_cfg/registry/modules.py
CHANGED
|
@@ -13,11 +13,8 @@ MODULES_REGISTRY = {
|
|
|
13
13
|
# Centrifugo module
|
|
14
14
|
"DjangoCfgCentrifugoConfig": ("django_cfg.apps.integrations.centrifugo.services.client.config", "DjangoCfgCentrifugoConfig"),
|
|
15
15
|
|
|
16
|
-
# gRPC module
|
|
16
|
+
# gRPC module (uses flat API - no nested config imports needed)
|
|
17
17
|
"GRPCConfig": ("django_cfg.models.api.grpc", "GRPCConfig"),
|
|
18
|
-
"GRPCServerConfig": ("django_cfg.models.api.grpc", "GRPCServerConfig"),
|
|
19
|
-
"GRPCAuthConfig": ("django_cfg.models.api.grpc", "GRPCAuthConfig"),
|
|
20
|
-
"GRPCProtoConfig": ("django_cfg.models.api.grpc", "GRPCProtoConfig"),
|
|
21
18
|
|
|
22
19
|
# Next.js Admin Integration
|
|
23
20
|
"NextJsAdminConfig": ("django_cfg.modules.nextjs_admin", "NextJsAdminConfig"),
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
2
|
+
# ⚠️ DJANGO-CFG LIBRARY - TESTING & DEVELOPMENT INSTRUCTIONS
|
|
3
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
4
|
+
#
|
|
5
|
+
# ❌ DO NOT test django-cfg library directly from this directory!
|
|
6
|
+
# ❌ DO NOT use pip install -r requirements.txt
|
|
7
|
+
# ❌ DO NOT use poetry install here
|
|
8
|
+
#
|
|
9
|
+
# ✅ This library is AUTOMATICALLY LINKED to the Django test project
|
|
10
|
+
# ✅ All changes in this library are IMMEDIATELY available in the test project
|
|
11
|
+
# ✅ NO installation or build steps needed - just edit and test!
|
|
12
|
+
#
|
|
13
|
+
# 📋 HOW TO TEST YOUR CHANGES:
|
|
14
|
+
#
|
|
15
|
+
# This library (django-cfg-dev) is automatically symlinked to:
|
|
16
|
+
# ../../../../solution/projects/django/
|
|
17
|
+
#
|
|
18
|
+
# All testing MUST be done through the Django project:
|
|
19
|
+
#
|
|
20
|
+
# cd ../../../../solution/projects/django
|
|
21
|
+
# poetry run python manage.py migrate
|
|
22
|
+
# poetry run python manage.py runserver
|
|
23
|
+
# poetry run python manage.py test
|
|
24
|
+
# poetry run pytest
|
|
25
|
+
#
|
|
26
|
+
# 🔄 WORKFLOW:
|
|
27
|
+
#
|
|
28
|
+
# 1. Edit django-cfg library code here (src/django_cfg/...)
|
|
29
|
+
# 2. Changes are INSTANTLY available in the Django project (via symlink)
|
|
30
|
+
# 3. Test your changes by running Django commands from ../../../../solution/projects/django
|
|
31
|
+
# 4. No rebuild or reinstall needed - it's live!
|
|
32
|
+
#
|
|
33
|
+
# 📁 PROJECT STRUCTURE:
|
|
34
|
+
#
|
|
35
|
+
# @projects/django-cfg/
|
|
36
|
+
# ├── projects/django-cfg-dev/ ← Django-CFG library (YOU ARE HERE)
|
|
37
|
+
# │ └── src/django_cfg/ ← Library source code
|
|
38
|
+
# └── solution/projects/django/ ← Django test project (GO HERE)
|
|
39
|
+
# ├── manage.py ← Run commands here
|
|
40
|
+
# ├── pyproject.toml ← Django project dependencies
|
|
41
|
+
# └── .venv/ ← Contains symlink to django-cfg-dev
|
|
42
|
+
#
|
|
43
|
+
# 🔗 SYMLINK DETAILS:
|
|
44
|
+
#
|
|
45
|
+
# The symlink is created automatically via: make install-local
|
|
46
|
+
# Located at: ../../../../solution/projects/django/.venv/lib/python3.12/site-packages/django_cfg
|
|
47
|
+
# Points to: projects/django-cfg-dev/src/django_cfg (this directory)
|
|
48
|
+
#
|
|
49
|
+
# 💡 TIP: If symlink is broken, run from django-cfg root:
|
|
50
|
+
# make install-local
|
|
51
|
+
#
|
|
52
|
+
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-cfg
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.14
|
|
4
4
|
Summary: Modern Django framework with type-safe Pydantic v2 configuration, Next.js admin integration, real-time WebSockets, and 8 enterprise apps. Replace settings.py with validated models, 90% less code. Production-ready with AI agents, auto-generated TypeScript clients, and zero-config features.
|
|
5
5
|
Project-URL: Homepage, https://djangocfg.com
|
|
6
6
|
Project-URL: Documentation, https://djangocfg.com
|