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
django_cfg/__init__.py
CHANGED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django CFG Commands API Serializers.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rest_framework import serializers
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CommandMetadataSerializer(serializers.Serializer):
|
|
9
|
+
"""
|
|
10
|
+
Serializer for command metadata.
|
|
11
|
+
|
|
12
|
+
Includes security attributes from our base classes:
|
|
13
|
+
- web_executable: Can be executed via web interface
|
|
14
|
+
- requires_input: Requires interactive user input
|
|
15
|
+
- is_destructive: Modifies or deletes data
|
|
16
|
+
- is_allowed: Passes security checks
|
|
17
|
+
- risk_level: Security risk assessment
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
web_executable = serializers.BooleanField(
|
|
21
|
+
allow_null=True,
|
|
22
|
+
help_text="Whether command can be executed via web interface"
|
|
23
|
+
)
|
|
24
|
+
requires_input = serializers.BooleanField(
|
|
25
|
+
allow_null=True,
|
|
26
|
+
help_text="Whether command requires interactive user input"
|
|
27
|
+
)
|
|
28
|
+
is_destructive = serializers.BooleanField(
|
|
29
|
+
allow_null=True,
|
|
30
|
+
help_text="Whether command modifies or deletes data"
|
|
31
|
+
)
|
|
32
|
+
is_allowed = serializers.BooleanField(
|
|
33
|
+
help_text="Whether command passes security checks for web execution"
|
|
34
|
+
)
|
|
35
|
+
risk_level = serializers.ChoiceField(
|
|
36
|
+
choices=['safe', 'caution', 'dangerous', 'unknown'],
|
|
37
|
+
help_text="Security risk level assessment"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CommandSerializer(serializers.Serializer):
|
|
42
|
+
"""
|
|
43
|
+
Serializer for Django management commands.
|
|
44
|
+
|
|
45
|
+
Returns command information including:
|
|
46
|
+
- Basic info (name, app, description)
|
|
47
|
+
- Security metadata (web_executable, requires_input, is_destructive)
|
|
48
|
+
- Execution status (is_allowed, risk_level)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
name = serializers.CharField(
|
|
52
|
+
help_text="Command name"
|
|
53
|
+
)
|
|
54
|
+
app = serializers.CharField(
|
|
55
|
+
help_text="Django app that provides this command"
|
|
56
|
+
)
|
|
57
|
+
description = serializers.CharField(
|
|
58
|
+
help_text="Short description of what the command does"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Security metadata - flattened for easier API consumption
|
|
62
|
+
web_executable = serializers.BooleanField(
|
|
63
|
+
allow_null=True,
|
|
64
|
+
help_text="Can be executed via web interface"
|
|
65
|
+
)
|
|
66
|
+
requires_input = serializers.BooleanField(
|
|
67
|
+
allow_null=True,
|
|
68
|
+
help_text="Requires interactive user input"
|
|
69
|
+
)
|
|
70
|
+
is_destructive = serializers.BooleanField(
|
|
71
|
+
allow_null=True,
|
|
72
|
+
help_text="Modifies or deletes data"
|
|
73
|
+
)
|
|
74
|
+
is_allowed = serializers.BooleanField(
|
|
75
|
+
help_text="Passes security checks"
|
|
76
|
+
)
|
|
77
|
+
risk_level = serializers.ChoiceField(
|
|
78
|
+
choices=['safe', 'caution', 'dangerous', 'unknown'],
|
|
79
|
+
help_text="Security risk level"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CommandListResponseSerializer(serializers.Serializer):
|
|
84
|
+
"""
|
|
85
|
+
Serializer for list commands API response.
|
|
86
|
+
|
|
87
|
+
Returns categorized commands:
|
|
88
|
+
- django_cfg: Custom django-cfg commands
|
|
89
|
+
- django_core: Django built-in commands
|
|
90
|
+
- third_party: Third-party app commands
|
|
91
|
+
- project: Project-specific commands
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
status = serializers.ChoiceField(
|
|
95
|
+
choices=['success', 'error'],
|
|
96
|
+
help_text="Response status"
|
|
97
|
+
)
|
|
98
|
+
commands = serializers.DictField(
|
|
99
|
+
child=serializers.ListField(child=CommandSerializer()),
|
|
100
|
+
help_text="Categorized command lists"
|
|
101
|
+
)
|
|
102
|
+
total_commands = serializers.IntegerField(
|
|
103
|
+
help_text="Total number of commands available"
|
|
104
|
+
)
|
|
105
|
+
timestamp = serializers.DateTimeField(
|
|
106
|
+
help_text="Response timestamp"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class CommandExecutionRequestSerializer(serializers.Serializer):
|
|
111
|
+
"""
|
|
112
|
+
Serializer for command execution requests.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
command = serializers.CharField(
|
|
116
|
+
required=True,
|
|
117
|
+
help_text="Name of the command to execute"
|
|
118
|
+
)
|
|
119
|
+
args = serializers.ListField(
|
|
120
|
+
child=serializers.CharField(),
|
|
121
|
+
required=False,
|
|
122
|
+
default=list,
|
|
123
|
+
help_text="Positional arguments for the command"
|
|
124
|
+
)
|
|
125
|
+
options = serializers.DictField(
|
|
126
|
+
required=False,
|
|
127
|
+
default=dict,
|
|
128
|
+
help_text="Named options for the command (e.g., {'--verbose': True})"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class CommandHelpResponseSerializer(serializers.Serializer):
|
|
133
|
+
"""
|
|
134
|
+
Serializer for command help API response.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
status = serializers.ChoiceField(
|
|
138
|
+
choices=['success', 'error'],
|
|
139
|
+
help_text="Response status"
|
|
140
|
+
)
|
|
141
|
+
command = serializers.CharField(
|
|
142
|
+
help_text="Command name"
|
|
143
|
+
)
|
|
144
|
+
app = serializers.CharField(
|
|
145
|
+
help_text="Django app"
|
|
146
|
+
)
|
|
147
|
+
help = serializers.CharField(
|
|
148
|
+
help_text="Full help text from command --help"
|
|
149
|
+
)
|
|
150
|
+
timestamp = serializers.DateTimeField(
|
|
151
|
+
help_text="Response timestamp"
|
|
152
|
+
)
|
|
@@ -49,6 +49,7 @@ def list_commands_view(request):
|
|
|
49
49
|
"name": command_name,
|
|
50
50
|
"app": app_name,
|
|
51
51
|
"description": _get_command_description(command_name),
|
|
52
|
+
**_get_command_metadata(command_name, app_name),
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
if app_name == "django_cfg":
|
|
@@ -270,6 +271,37 @@ def _get_command_description(command_name: str) -> str:
|
|
|
270
271
|
return f'Django management command: {command_name}'
|
|
271
272
|
|
|
272
273
|
|
|
274
|
+
def _get_command_metadata(command_name: str, app_name: str) -> dict:
|
|
275
|
+
"""
|
|
276
|
+
Get security metadata for a command.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Dict with web_executable, requires_input, is_destructive, is_allowed
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
from django.core.management import load_command_class
|
|
283
|
+
from django_cfg.apps.system.dashboard.services.commands_security import is_command_allowed, get_command_risk_level
|
|
284
|
+
|
|
285
|
+
command_instance = load_command_class(app_name, command_name)
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
'web_executable': getattr(command_instance, 'web_executable', None),
|
|
289
|
+
'requires_input': getattr(command_instance, 'requires_input', None),
|
|
290
|
+
'is_destructive': getattr(command_instance, 'is_destructive', None),
|
|
291
|
+
'is_allowed': is_command_allowed(command_name, app_name),
|
|
292
|
+
'risk_level': get_command_risk_level(command_name, app_name),
|
|
293
|
+
}
|
|
294
|
+
except Exception as e:
|
|
295
|
+
logger.debug(f"Could not load metadata for {command_name}: {e}")
|
|
296
|
+
return {
|
|
297
|
+
'web_executable': None,
|
|
298
|
+
'requires_input': None,
|
|
299
|
+
'is_destructive': None,
|
|
300
|
+
'is_allowed': False,
|
|
301
|
+
'risk_level': 'unknown',
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
273
305
|
def _get_command_help(command_name: str) -> str:
|
|
274
306
|
"""Get full help text for a command."""
|
|
275
307
|
try:
|
|
@@ -2,17 +2,20 @@
|
|
|
2
2
|
Management command to test OTP functionality.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from django.core.management.base import
|
|
5
|
+
from django.core.management.base import CommandError
|
|
6
6
|
from django.utils import timezone
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from rich.panel import Panel
|
|
9
9
|
from rich.table import Table
|
|
10
10
|
|
|
11
|
+
from django_cfg.management.utils import SafeCommand
|
|
12
|
+
|
|
11
13
|
from ...models import OTPSecret
|
|
12
14
|
from ...services.otp_service import OTPService
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
class Command(
|
|
17
|
+
class Command(SafeCommand):
|
|
18
|
+
command_name = 'otp_test'
|
|
16
19
|
help = "Test OTP functionality with email and telegram delivery"
|
|
17
20
|
|
|
18
21
|
def __init__(self, *args, **kwargs):
|
|
@@ -4,6 +4,24 @@ from rest_framework import serializers
|
|
|
4
4
|
from ..models import CustomUser, RegistrationSource, UserRegistrationSource
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
class CentrifugoTokenSerializer(serializers.Serializer):
|
|
8
|
+
"""Nested serializer for Centrifugo WebSocket connection token."""
|
|
9
|
+
|
|
10
|
+
token = serializers.CharField(
|
|
11
|
+
help_text="JWT token for Centrifugo WebSocket connection"
|
|
12
|
+
)
|
|
13
|
+
centrifugo_url = serializers.URLField(
|
|
14
|
+
help_text="Centrifugo WebSocket URL"
|
|
15
|
+
)
|
|
16
|
+
expires_at = serializers.DateTimeField(
|
|
17
|
+
help_text="Token expiration time (ISO 8601)"
|
|
18
|
+
)
|
|
19
|
+
channels = serializers.ListField(
|
|
20
|
+
child=serializers.CharField(),
|
|
21
|
+
help_text="List of allowed channels for this user"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
7
25
|
class UserSerializer(serializers.ModelSerializer):
|
|
8
26
|
"""Serializer for user details."""
|
|
9
27
|
|
|
@@ -11,6 +29,7 @@ class UserSerializer(serializers.ModelSerializer):
|
|
|
11
29
|
initials = serializers.ReadOnlyField()
|
|
12
30
|
display_username = serializers.ReadOnlyField()
|
|
13
31
|
avatar = serializers.SerializerMethodField()
|
|
32
|
+
centrifugo = serializers.SerializerMethodField()
|
|
14
33
|
|
|
15
34
|
class Meta:
|
|
16
35
|
model = CustomUser
|
|
@@ -31,6 +50,7 @@ class UserSerializer(serializers.ModelSerializer):
|
|
|
31
50
|
"date_joined",
|
|
32
51
|
"last_login",
|
|
33
52
|
"unanswered_messages_count",
|
|
53
|
+
"centrifugo",
|
|
34
54
|
]
|
|
35
55
|
read_only_fields = [
|
|
36
56
|
"id",
|
|
@@ -52,6 +72,28 @@ class UserSerializer(serializers.ModelSerializer):
|
|
|
52
72
|
return obj.avatar.url
|
|
53
73
|
return None
|
|
54
74
|
|
|
75
|
+
@extend_schema_field(CentrifugoTokenSerializer(allow_null=True))
|
|
76
|
+
def get_centrifugo(self, obj):
|
|
77
|
+
"""
|
|
78
|
+
Generate Centrifugo WebSocket connection token if enabled.
|
|
79
|
+
|
|
80
|
+
Returns None if Centrifugo is disabled in config.
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
# Import here to avoid circular imports
|
|
84
|
+
from django_cfg.apps.integrations.centrifugo.services import generate_centrifugo_token
|
|
85
|
+
|
|
86
|
+
# Generate token with user's channels
|
|
87
|
+
token_data = generate_centrifugo_token(obj)
|
|
88
|
+
return token_data
|
|
89
|
+
|
|
90
|
+
except ValueError:
|
|
91
|
+
# Centrifugo not configured or disabled
|
|
92
|
+
return None
|
|
93
|
+
except Exception:
|
|
94
|
+
# If token generation fails, return None (don't break profile response)
|
|
95
|
+
return None
|
|
96
|
+
|
|
55
97
|
|
|
56
98
|
|
|
57
99
|
class UserProfileUpdateSerializer(serializers.ModelSerializer):
|
|
@@ -5,14 +5,17 @@ Management command to create agent definitions.
|
|
|
5
5
|
import asyncio
|
|
6
6
|
|
|
7
7
|
from django.contrib.auth.models import User
|
|
8
|
-
from django.core.management.base import
|
|
8
|
+
from django.core.management.base import CommandError
|
|
9
|
+
|
|
10
|
+
from django_cfg.management.utils import AdminCommand
|
|
9
11
|
|
|
10
12
|
from django_cfg.apps.business.agents.models.registry import AgentDefinition
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
class Command(
|
|
15
|
+
class Command(AdminCommand):
|
|
14
16
|
"""Create agent definition from command line."""
|
|
15
17
|
|
|
18
|
+
command_name = 'create_agent'
|
|
16
19
|
help = 'Create a new agent definition'
|
|
17
20
|
|
|
18
21
|
def add_arguments(self, parser):
|
|
@@ -172,195 +175,3 @@ class Command(BaseCommand):
|
|
|
172
175
|
return await User.objects.filter(is_superuser=True).afirst()
|
|
173
176
|
except User.DoesNotExist:
|
|
174
177
|
raise CommandError("No superuser found. Please create a superuser first or specify --creator")
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
class Command(BaseCommand):
|
|
178
|
-
"""Load agent definitions from templates."""
|
|
179
|
-
|
|
180
|
-
help = 'Load pre-built agent templates'
|
|
181
|
-
|
|
182
|
-
def add_arguments(self, parser):
|
|
183
|
-
"""Add command arguments."""
|
|
184
|
-
parser.add_argument(
|
|
185
|
-
'--list',
|
|
186
|
-
action='store_true',
|
|
187
|
-
help='List available templates'
|
|
188
|
-
)
|
|
189
|
-
parser.add_argument(
|
|
190
|
-
'--load',
|
|
191
|
-
type=str,
|
|
192
|
-
nargs='*',
|
|
193
|
-
help='Load specific templates (space-separated names)'
|
|
194
|
-
)
|
|
195
|
-
parser.add_argument(
|
|
196
|
-
'--load-all',
|
|
197
|
-
action='store_true',
|
|
198
|
-
help='Load all available templates'
|
|
199
|
-
)
|
|
200
|
-
parser.add_argument(
|
|
201
|
-
'--creator',
|
|
202
|
-
type=str,
|
|
203
|
-
help='Username of agent creator (defaults to first superuser)'
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
def handle(self, *args, **options):
|
|
207
|
-
"""Handle command execution."""
|
|
208
|
-
if options['list']:
|
|
209
|
-
self._list_templates()
|
|
210
|
-
elif options['load'] or options['load_all']:
|
|
211
|
-
asyncio.run(self._load_templates(options))
|
|
212
|
-
else:
|
|
213
|
-
self.stdout.write(
|
|
214
|
-
self.style.ERROR('Please specify --list, --load, or --load-all')
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
def _list_templates(self):
|
|
218
|
-
"""List available templates."""
|
|
219
|
-
templates = self._get_available_templates()
|
|
220
|
-
|
|
221
|
-
self.stdout.write(self.style.SUCCESS('š Available Agent Templates:'))
|
|
222
|
-
self.stdout.write('=' * 40)
|
|
223
|
-
|
|
224
|
-
for category, agents in templates.items():
|
|
225
|
-
self.stdout.write(f"\n{category.upper()}:")
|
|
226
|
-
for agent_name, agent_info in agents.items():
|
|
227
|
-
self.stdout.write(f" ⢠{agent_name}: {agent_info['description']}")
|
|
228
|
-
|
|
229
|
-
async def _load_templates(self, options):
|
|
230
|
-
"""Load templates."""
|
|
231
|
-
creator = await self._get_creator_user(options.get('creator'))
|
|
232
|
-
templates = self._get_available_templates()
|
|
233
|
-
|
|
234
|
-
if options['load_all']:
|
|
235
|
-
# Load all templates
|
|
236
|
-
to_load = []
|
|
237
|
-
for category_templates in templates.values():
|
|
238
|
-
to_load.extend(category_templates.keys())
|
|
239
|
-
else:
|
|
240
|
-
to_load = options['load']
|
|
241
|
-
|
|
242
|
-
loaded_count = 0
|
|
243
|
-
|
|
244
|
-
for template_name in to_load:
|
|
245
|
-
# Find template
|
|
246
|
-
template_info = None
|
|
247
|
-
for category_templates in templates.values():
|
|
248
|
-
if template_name in category_templates:
|
|
249
|
-
template_info = category_templates[template_name]
|
|
250
|
-
break
|
|
251
|
-
|
|
252
|
-
if not template_info:
|
|
253
|
-
self.stdout.write(
|
|
254
|
-
self.style.WARNING(f"Template '{template_name}' not found")
|
|
255
|
-
)
|
|
256
|
-
continue
|
|
257
|
-
|
|
258
|
-
# Check if agent already exists
|
|
259
|
-
if await AgentDefinition.objects.filter(name=template_name).aexists():
|
|
260
|
-
self.stdout.write(
|
|
261
|
-
self.style.WARNING(f"Agent '{template_name}' already exists, skipping")
|
|
262
|
-
)
|
|
263
|
-
continue
|
|
264
|
-
|
|
265
|
-
# Create agent
|
|
266
|
-
try:
|
|
267
|
-
agent_data = template_info.copy()
|
|
268
|
-
agent_data['name'] = template_name
|
|
269
|
-
agent_data['created_by'] = creator
|
|
270
|
-
|
|
271
|
-
await AgentDefinition.objects.acreate(**agent_data)
|
|
272
|
-
|
|
273
|
-
self.stdout.write(
|
|
274
|
-
self.style.SUCCESS(f"ā
Loaded template: {template_name}")
|
|
275
|
-
)
|
|
276
|
-
loaded_count += 1
|
|
277
|
-
|
|
278
|
-
except Exception as e:
|
|
279
|
-
self.stdout.write(
|
|
280
|
-
self.style.ERROR(f"Failed to load template '{template_name}': {e}")
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
self.stdout.write(
|
|
284
|
-
self.style.SUCCESS(f"\nš Loaded {loaded_count} agent templates")
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
def _get_available_templates(self):
|
|
288
|
-
"""Get available agent templates."""
|
|
289
|
-
return {
|
|
290
|
-
'content': {
|
|
291
|
-
'content_analyzer': {
|
|
292
|
-
'description': 'Analyze content sentiment, topics, and quality',
|
|
293
|
-
'instructions': 'Analyze content for sentiment, topics, keywords, and quality metrics.',
|
|
294
|
-
'deps_type': 'ContentDeps',
|
|
295
|
-
'output_type': 'AnalysisResult',
|
|
296
|
-
'category': 'content',
|
|
297
|
-
'model': 'openai:gpt-4o-mini',
|
|
298
|
-
},
|
|
299
|
-
'content_generator': {
|
|
300
|
-
'description': 'Generate high-quality content based on requirements',
|
|
301
|
-
'instructions': 'Generate engaging, well-structured content based on type, audience, and style requirements.',
|
|
302
|
-
'deps_type': 'ContentDeps',
|
|
303
|
-
'output_type': 'ProcessResult',
|
|
304
|
-
'category': 'content',
|
|
305
|
-
'model': 'openai:gpt-4o-mini',
|
|
306
|
-
},
|
|
307
|
-
'content_validator': {
|
|
308
|
-
'description': 'Validate content quality and compliance',
|
|
309
|
-
'instructions': 'Validate content for grammar, style, accuracy, and guideline compliance.',
|
|
310
|
-
'deps_type': 'ContentDeps',
|
|
311
|
-
'output_type': 'ValidationResult',
|
|
312
|
-
'category': 'content',
|
|
313
|
-
'model': 'openai:gpt-4o-mini',
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
'data': {
|
|
317
|
-
'data_processor': {
|
|
318
|
-
'description': 'Process and transform data',
|
|
319
|
-
'instructions': 'Process, clean, and transform data according to specifications.',
|
|
320
|
-
'deps_type': 'DataProcessingDeps',
|
|
321
|
-
'output_type': 'ProcessResult',
|
|
322
|
-
'category': 'data',
|
|
323
|
-
'model': 'openai:gpt-4o-mini',
|
|
324
|
-
},
|
|
325
|
-
'data_validator': {
|
|
326
|
-
'description': 'Validate data quality and integrity',
|
|
327
|
-
'instructions': 'Validate data quality, check for errors, and ensure integrity.',
|
|
328
|
-
'deps_type': 'DataProcessingDeps',
|
|
329
|
-
'output_type': 'ValidationResult',
|
|
330
|
-
'category': 'data',
|
|
331
|
-
'model': 'openai:gpt-4o-mini',
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
'business': {
|
|
335
|
-
'business_rules': {
|
|
336
|
-
'description': 'Apply business rules and logic',
|
|
337
|
-
'instructions': 'Apply business rules, validate decisions, and ensure compliance.',
|
|
338
|
-
'deps_type': 'BusinessLogicDeps',
|
|
339
|
-
'output_type': 'ProcessResult',
|
|
340
|
-
'category': 'business',
|
|
341
|
-
'model': 'openai:gpt-4o-mini',
|
|
342
|
-
},
|
|
343
|
-
'decision_maker': {
|
|
344
|
-
'description': 'Make decisions based on criteria',
|
|
345
|
-
'instructions': 'Analyze options and make informed decisions based on criteria and context.',
|
|
346
|
-
'deps_type': 'BusinessLogicDeps',
|
|
347
|
-
'output_type': 'ProcessResult',
|
|
348
|
-
'category': 'business',
|
|
349
|
-
'model': 'openai:gpt-4o-mini',
|
|
350
|
-
},
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
async def _get_creator_user(self, username):
|
|
355
|
-
"""Get creator user."""
|
|
356
|
-
if username:
|
|
357
|
-
try:
|
|
358
|
-
return await User.objects.aget(username=username)
|
|
359
|
-
except User.DoesNotExist:
|
|
360
|
-
raise CommandError(f"User '{username}' not found")
|
|
361
|
-
else:
|
|
362
|
-
# Use first superuser
|
|
363
|
-
try:
|
|
364
|
-
return await User.objects.filter(is_superuser=True).afirst()
|
|
365
|
-
except User.DoesNotExist:
|
|
366
|
-
raise CommandError("No superuser found. Please create a superuser first or specify --creator")
|