django-cfg 1.4.10__py3-none-any.whl → 1.4.11__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.
- django_cfg/apps/agents/management/commands/create_agent.py +1 -1
- django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
- django_cfg/apps/newsletter/serializers.py +40 -3
- django_cfg/apps/newsletter/views/campaigns.py +12 -3
- django_cfg/apps/newsletter/views/emails.py +14 -3
- django_cfg/apps/newsletter/views/subscriptions.py +12 -2
- django_cfg/apps/payments/views/api/currencies.py +49 -6
- django_cfg/apps/payments/views/api/webhooks.py +72 -7
- django_cfg/apps/payments/views/overview/serializers.py +34 -1
- django_cfg/apps/payments/views/overview/views.py +2 -1
- django_cfg/apps/payments/views/serializers/payments.py +6 -6
- django_cfg/apps/urls.py +106 -45
- django_cfg/core/base/config_model.py +2 -2
- django_cfg/core/constants.py +1 -1
- django_cfg/core/generation/integration_generators/__init__.py +1 -1
- django_cfg/core/generation/integration_generators/api.py +72 -49
- django_cfg/core/integration/display/startup.py +30 -22
- django_cfg/core/integration/url_integration.py +15 -16
- django_cfg/dashboard/sections/documentation.py +391 -0
- django_cfg/management/commands/check_endpoints.py +11 -160
- django_cfg/management/commands/check_settings.py +13 -348
- django_cfg/management/commands/clear_constance.py +13 -201
- django_cfg/management/commands/create_token.py +13 -321
- django_cfg/management/commands/generate_clients.py +23 -0
- django_cfg/management/commands/list_urls.py +13 -306
- django_cfg/management/commands/migrate_all.py +13 -126
- django_cfg/management/commands/migrator.py +13 -396
- django_cfg/management/commands/rundramatiq.py +15 -247
- django_cfg/management/commands/rundramatiq_simulator.py +12 -429
- django_cfg/management/commands/runserver_ngrok.py +15 -160
- django_cfg/management/commands/script.py +12 -488
- django_cfg/management/commands/show_config.py +12 -215
- django_cfg/management/commands/show_urls.py +12 -342
- django_cfg/management/commands/superuser.py +15 -295
- django_cfg/management/commands/task_clear.py +14 -217
- django_cfg/management/commands/task_status.py +13 -248
- django_cfg/management/commands/test_email.py +15 -86
- django_cfg/management/commands/test_telegram.py +14 -61
- django_cfg/management/commands/test_twilio.py +15 -105
- django_cfg/management/commands/tree.py +13 -383
- django_cfg/management/commands/validate_openapi.py +10 -0
- django_cfg/middleware/README.md +1 -1
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/__init__.py +2 -2
- django_cfg/models/api/drf/spectacular.py +6 -6
- django_cfg/models/django/__init__.py +2 -2
- django_cfg/models/django/openapi.py +238 -0
- django_cfg/modules/django_admin/management/__init__.py +0 -0
- django_cfg/modules/django_admin/management/commands/__init__.py +0 -0
- django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
- django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
- django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
- django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
- django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
- django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
- django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
- django_cfg/modules/django_admin/management/commands/script.py +496 -0
- django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
- django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
- django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
- django_cfg/modules/django_admin/management/commands/tree.py +390 -0
- django_cfg/modules/django_client/__init__.py +20 -0
- django_cfg/modules/django_client/apps.py +35 -0
- django_cfg/modules/django_client/core/__init__.py +56 -0
- django_cfg/modules/django_client/core/archive/__init__.py +11 -0
- django_cfg/modules/django_client/core/archive/manager.py +134 -0
- django_cfg/modules/django_client/core/cli/__init__.py +12 -0
- django_cfg/modules/django_client/core/cli/main.py +235 -0
- django_cfg/modules/django_client/core/config/__init__.py +18 -0
- django_cfg/modules/django_client/core/config/config.py +188 -0
- django_cfg/modules/django_client/core/config/group.py +101 -0
- django_cfg/modules/django_client/core/config/service.py +209 -0
- django_cfg/modules/django_client/core/generator/__init__.py +115 -0
- django_cfg/modules/django_client/core/generator/base.py +767 -0
- django_cfg/modules/django_client/core/generator/python.py +751 -0
- django_cfg/modules/django_client/core/generator/templates/python/__init__.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/python/api_wrapper.py.jinja +130 -0
- django_cfg/modules/django_client/core/generator/templates/python/app_init.py.jinja +6 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/app_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/flat_client.py.jinja +38 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/main_client.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/main_client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/operation_method.py.jinja +7 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/sub_client.py.jinja +11 -0
- django_cfg/modules/django_client/core/generator/templates/python/client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/templates/python/main_init.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/app_models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/enum_class.py.jinja +15 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/enums.py.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/schema_class.py.jinja +19 -0
- django_cfg/modules/django_client/core/generator/templates/python/utils/logger.py.jinja +255 -0
- django_cfg/modules/django_client/core/generator/templates/python/utils/schema.py.jinja +12 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/app_index.ts.jinja +2 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/app_client.ts.jinja +18 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/client.ts.jinja +327 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/flat_client.ts.jinja +109 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/main_client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/operation.ts.jinja +61 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/sub_client.ts.jinja +15 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/index.ts.jinja +5 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/main_index.ts.jinja +206 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/app_models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/enums.ts.jinja +4 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/errors.ts.jinja +114 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/http.ts.jinja +98 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/logger.ts.jinja +251 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/schema.ts.jinja +7 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/storage.ts.jinja +114 -0
- django_cfg/modules/django_client/core/generator/typescript.py +872 -0
- django_cfg/modules/django_client/core/groups/__init__.py +13 -0
- django_cfg/modules/django_client/core/groups/detector.py +178 -0
- django_cfg/modules/django_client/core/groups/manager.py +314 -0
- django_cfg/modules/django_client/core/ir/__init__.py +57 -0
- django_cfg/modules/django_client/core/ir/context.py +387 -0
- django_cfg/modules/django_client/core/ir/operation.py +518 -0
- django_cfg/modules/django_client/core/ir/schema.py +353 -0
- django_cfg/modules/django_client/core/parser/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/base.py +648 -0
- django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/models/base.py +212 -0
- django_cfg/modules/django_client/core/parser/models/components.py +160 -0
- django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
- django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
- django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
- django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
- django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
- django_cfg/modules/django_client/core/validation/__init__.py +22 -0
- django_cfg/modules/django_client/core/validation/checker.py +134 -0
- django_cfg/modules/django_client/core/validation/fixer.py +216 -0
- django_cfg/modules/django_client/core/validation/reporter.py +480 -0
- django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
- django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
- django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
- django_cfg/modules/django_client/core/validation/safety.py +266 -0
- django_cfg/modules/django_client/management/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +422 -0
- django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
- django_cfg/modules/django_client/spectacular/__init__.py +9 -0
- django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
- django_cfg/modules/django_client/urls.py +72 -0
- django_cfg/modules/django_email/management/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/test_email.py +93 -0
- django_cfg/modules/django_logging/django_logger.py +6 -6
- django_cfg/modules/django_ngrok/management/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
- django_cfg/modules/django_tasks/management/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
- django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
- django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
- django_cfg/modules/django_telegram/management/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
- django_cfg/modules/django_twilio/management/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
- django_cfg/modules/django_unfold/callbacks/main.py +16 -5
- django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
- django_cfg/pyproject.toml +2 -6
- django_cfg/registry/third_party.py +5 -7
- django_cfg/routing/callbacks.py +1 -1
- django_cfg/static/admin/css/prose-unfold.css +666 -0
- django_cfg/templates/admin/index.html +8 -0
- django_cfg/templates/admin/index_new.html +13 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
- django_cfg/templates/admin/sections/documentation_section.html +172 -0
- django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/METADATA +2 -2
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/RECORD +180 -59
- django_cfg/management/commands/generate.py +0 -107
- /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/licenses/LICENSE +0 -0
@@ -1,328 +1,20 @@
|
|
1
1
|
"""
|
2
|
-
|
3
|
-
Generate API tokens and authentication tokens.
|
4
|
-
"""
|
5
|
-
|
6
|
-
import os
|
7
|
-
import secrets
|
8
|
-
import string
|
9
|
-
from pathlib import Path
|
10
|
-
from django.core.management.base import BaseCommand
|
11
|
-
from django.contrib.auth import get_user_model
|
12
|
-
from django.conf import settings
|
13
|
-
import questionary
|
14
|
-
from datetime import datetime, timedelta
|
15
|
-
from django_cfg.modules.django_logging import get_logger
|
16
|
-
|
17
|
-
User = get_user_model()
|
18
|
-
|
19
|
-
|
20
|
-
logger = get_logger('create_token')
|
21
|
-
|
22
|
-
class Command(BaseCommand):
|
23
|
-
# Web execution metadata
|
24
|
-
web_executable = False
|
25
|
-
requires_input = True
|
26
|
-
is_destructive = False
|
27
|
-
|
28
|
-
help = 'Create API tokens and authentication tokens'
|
29
|
-
|
30
|
-
def add_arguments(self, parser):
|
31
|
-
parser.add_argument(
|
32
|
-
'--user',
|
33
|
-
type=str,
|
34
|
-
help='Username to create token for'
|
35
|
-
)
|
36
|
-
parser.add_argument(
|
37
|
-
'--type',
|
38
|
-
type=str,
|
39
|
-
choices=['api', 'auth', 'secret'],
|
40
|
-
help='Type of token to create'
|
41
|
-
)
|
42
|
-
parser.add_argument(
|
43
|
-
'--length',
|
44
|
-
type=int,
|
45
|
-
default=32,
|
46
|
-
help='Token length (default: 32)'
|
47
|
-
)
|
48
|
-
parser.add_argument(
|
49
|
-
'--expires',
|
50
|
-
type=int,
|
51
|
-
help='Token expiration in days'
|
52
|
-
)
|
53
|
-
|
54
|
-
def handle(self, *args, **options):
|
55
|
-
logger.info("Starting create_token command")
|
56
|
-
if options['user'] and options['type']:
|
57
|
-
self.create_token_for_user(
|
58
|
-
username=options['user'],
|
59
|
-
token_type=options['type'],
|
60
|
-
length=options['length'],
|
61
|
-
expires_days=options['expires']
|
62
|
-
)
|
63
|
-
else:
|
64
|
-
self.show_interactive_menu()
|
2
|
+
Django-CFG wrapper for create_token command.
|
65
3
|
|
66
|
-
|
67
|
-
|
68
|
-
self.stdout.write(self.style.SUCCESS('\n🔑 Token Creation Tool - Django Config Toolkit\n'))
|
4
|
+
This is a simple alias for django_admin.management.commands.create_token.
|
5
|
+
All logic is in django_admin module.
|
69
6
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
questionary.Choice('🔒 Create Secret Key', value='secret'),
|
74
|
-
questionary.Choice('👤 Create Token for User', value='user'),
|
75
|
-
questionary.Choice('📝 Generate Django Secret Key', value='django_secret'),
|
76
|
-
questionary.Choice('❌ Exit', value='exit')
|
77
|
-
]
|
78
|
-
|
79
|
-
choice = questionary.select(
|
80
|
-
'Select token type:',
|
81
|
-
choices=choices
|
82
|
-
).ask()
|
83
|
-
|
84
|
-
if choice == 'api':
|
85
|
-
self.create_api_token()
|
86
|
-
elif choice == 'auth':
|
87
|
-
self.create_auth_token()
|
88
|
-
elif choice == 'secret':
|
89
|
-
self.create_secret_key()
|
90
|
-
elif choice == 'user':
|
91
|
-
self.create_token_for_user_interactive()
|
92
|
-
elif choice == 'django_secret':
|
93
|
-
self.generate_django_secret_key()
|
94
|
-
elif choice == 'exit':
|
95
|
-
self.stdout.write('Goodbye! 👋')
|
96
|
-
return
|
97
|
-
|
98
|
-
def create_api_token(self):
|
99
|
-
"""Create API token"""
|
100
|
-
self.stdout.write(self.style.SUCCESS('🔑 Creating API Token...'))
|
101
|
-
|
102
|
-
# Get token details
|
103
|
-
token_name = questionary.text('Token name:').ask()
|
104
|
-
if not token_name:
|
105
|
-
self.stdout.write(self.style.ERROR('❌ Token name is required'))
|
106
|
-
return
|
107
|
-
|
108
|
-
token_length = questionary.select(
|
109
|
-
'Token length:',
|
110
|
-
choices=['32', '64', '128', '256']
|
111
|
-
).ask()
|
112
|
-
|
113
|
-
expires = questionary.select(
|
114
|
-
'Token expiration:',
|
115
|
-
choices=['Never', '30 days', '90 days', '1 year']
|
116
|
-
).ask()
|
117
|
-
|
118
|
-
# Generate token
|
119
|
-
token = self.generate_token(int(token_length))
|
120
|
-
|
121
|
-
# Calculate expiration
|
122
|
-
expiration_date = None
|
123
|
-
if expires != 'Never':
|
124
|
-
days_map = {
|
125
|
-
'30 days': 30,
|
126
|
-
'90 days': 90,
|
127
|
-
'1 year': 365
|
128
|
-
}
|
129
|
-
expiration_date = datetime.now() + timedelta(days=days_map[expires])
|
130
|
-
|
131
|
-
# Save token (in a real app, you'd save to database)
|
132
|
-
self.save_token_to_file('api_token', token, token_name, expiration_date)
|
133
|
-
|
134
|
-
self.stdout.write(self.style.SUCCESS(f'✅ API Token created: {token}'))
|
135
|
-
self.stdout.write(f'📝 Name: {token_name}')
|
136
|
-
if expiration_date:
|
137
|
-
self.stdout.write(f'⏰ Expires: {expiration_date.strftime("%Y-%m-%d %H:%M:%S")}')
|
138
|
-
|
139
|
-
def create_auth_token(self):
|
140
|
-
"""Create authentication token"""
|
141
|
-
self.stdout.write(self.style.SUCCESS('🔐 Creating Auth Token...'))
|
142
|
-
|
143
|
-
# Get token details
|
144
|
-
token_name = questionary.text('Token name:').ask()
|
145
|
-
if not token_name:
|
146
|
-
self.stdout.write(self.style.ERROR('❌ Token name is required'))
|
147
|
-
return
|
148
|
-
|
149
|
-
token_length = questionary.select(
|
150
|
-
'Token length:',
|
151
|
-
choices=['32', '64', '128']
|
152
|
-
).ask()
|
153
|
-
|
154
|
-
# Generate token
|
155
|
-
token = self.generate_token(int(token_length))
|
156
|
-
|
157
|
-
# Save token
|
158
|
-
self.save_token_to_file('auth_token', token, token_name)
|
159
|
-
|
160
|
-
self.stdout.write(self.style.SUCCESS(f'✅ Auth Token created: {token}'))
|
161
|
-
self.stdout.write(f'📝 Name: {token_name}')
|
162
|
-
|
163
|
-
def create_secret_key(self):
|
164
|
-
"""Create secret key"""
|
165
|
-
self.stdout.write(self.style.SUCCESS('🔒 Creating Secret Key...'))
|
166
|
-
|
167
|
-
# Get key details
|
168
|
-
key_name = questionary.text('Secret key name:').ask()
|
169
|
-
if not key_name:
|
170
|
-
self.stdout.write(self.style.ERROR('❌ Key name is required'))
|
171
|
-
return
|
172
|
-
|
173
|
-
key_length = questionary.select(
|
174
|
-
'Key length:',
|
175
|
-
choices=['32', '64', '128', '256']
|
176
|
-
).ask()
|
177
|
-
|
178
|
-
# Generate secret key
|
179
|
-
secret_key = self.generate_secret_key(int(key_length))
|
180
|
-
|
181
|
-
# Save key
|
182
|
-
self.save_token_to_file('secret_key', secret_key, key_name)
|
183
|
-
|
184
|
-
self.stdout.write(self.style.SUCCESS(f'✅ Secret Key created: {secret_key}'))
|
185
|
-
self.stdout.write(f'📝 Name: {key_name}')
|
186
|
-
|
187
|
-
def create_token_for_user_interactive(self):
|
188
|
-
"""Create token for user interactively"""
|
189
|
-
self.stdout.write(self.style.SUCCESS('👤 Creating Token for User...'))
|
190
|
-
|
191
|
-
# Get user
|
192
|
-
username = questionary.text('Username:').ask()
|
193
|
-
if not username:
|
194
|
-
self.stdout.write(self.style.ERROR('❌ Username is required'))
|
195
|
-
return
|
196
|
-
|
197
|
-
# Check if user exists
|
198
|
-
try:
|
199
|
-
user = User.objects.get(username=username)
|
200
|
-
except User.DoesNotExist:
|
201
|
-
self.stdout.write(self.style.ERROR(f'❌ User {username} does not exist'))
|
202
|
-
return
|
203
|
-
|
204
|
-
# Get token type
|
205
|
-
token_type = questionary.select(
|
206
|
-
'Token type:',
|
207
|
-
choices=['API Token', 'Auth Token', 'Secret Key']
|
208
|
-
).ask()
|
209
|
-
|
210
|
-
# Get token length
|
211
|
-
token_length = questionary.select(
|
212
|
-
'Token length:',
|
213
|
-
choices=['32', '64', '128']
|
214
|
-
).ask()
|
215
|
-
|
216
|
-
# Get expiration
|
217
|
-
expires = questionary.select(
|
218
|
-
'Token expiration:',
|
219
|
-
choices=['Never', '30 days', '90 days', '1 year']
|
220
|
-
).ask()
|
221
|
-
|
222
|
-
# Create token
|
223
|
-
self.create_token_for_user(
|
224
|
-
username=username,
|
225
|
-
token_type=token_type.lower().replace(' ', '_'),
|
226
|
-
length=int(token_length),
|
227
|
-
expires_days=None if expires == 'Never' else {
|
228
|
-
'30 days': 30,
|
229
|
-
'90 days': 90,
|
230
|
-
'1 year': 365
|
231
|
-
}[expires]
|
232
|
-
)
|
233
|
-
|
234
|
-
def create_token_for_user(self, username, token_type, length=32, expires_days=None):
|
235
|
-
"""Create token for specific user"""
|
236
|
-
try:
|
237
|
-
user = User.objects.get(username=username)
|
238
|
-
except User.DoesNotExist:
|
239
|
-
self.stdout.write(self.style.ERROR(f'❌ User {username} does not exist'))
|
240
|
-
return
|
241
|
-
|
242
|
-
# Generate token
|
243
|
-
if token_type == 'secret_key':
|
244
|
-
token = self.generate_secret_key(length)
|
245
|
-
else:
|
246
|
-
token = self.generate_token(length)
|
247
|
-
|
248
|
-
# Calculate expiration
|
249
|
-
expiration_date = None
|
250
|
-
if expires_days:
|
251
|
-
expiration_date = datetime.now() + timedelta(days=expires_days)
|
252
|
-
|
253
|
-
# Save token
|
254
|
-
token_name = f"{username}_{token_type}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
255
|
-
self.save_token_to_file(token_type, token, token_name, expiration_date, user)
|
256
|
-
|
257
|
-
self.stdout.write(self.style.SUCCESS(f'✅ {token_type.title()} created for {username}'))
|
258
|
-
self.stdout.write(f'🔑 Token: {token}')
|
259
|
-
self.stdout.write(f'📝 Name: {token_name}')
|
260
|
-
if expiration_date:
|
261
|
-
self.stdout.write(f'⏰ Expires: {expiration_date.strftime("%Y-%m-%d %H:%M:%S")}')
|
262
|
-
|
263
|
-
def generate_django_secret_key(self):
|
264
|
-
"""Generate Django secret key"""
|
265
|
-
self.stdout.write(self.style.SUCCESS('🔐 Generating Django Secret Key...'))
|
266
|
-
|
267
|
-
# Generate Django-compatible secret key
|
268
|
-
secret_key = self.generate_django_secret()
|
269
|
-
|
270
|
-
# Save to file
|
271
|
-
self.save_token_to_file('django_secret', secret_key, 'django_secret_key')
|
272
|
-
|
273
|
-
self.stdout.write(self.style.SUCCESS(f'✅ Django Secret Key generated: {secret_key}'))
|
274
|
-
self.stdout.write('💡 Add this to your .env file as SECRET_KEY=...')
|
7
|
+
Usage:
|
8
|
+
python manage.py create_token
|
9
|
+
"""
|
275
10
|
|
276
|
-
|
277
|
-
"""Generate random token"""
|
278
|
-
alphabet = string.ascii_letters + string.digits
|
279
|
-
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
11
|
+
from django_cfg.modules.django_admin.management.commands.create_token import Command as CreateTokenCommand
|
280
12
|
|
281
|
-
def generate_secret_key(self, length=64):
|
282
|
-
"""Generate secret key"""
|
283
|
-
alphabet = string.ascii_letters + string.digits + string.punctuation
|
284
|
-
# Remove characters that might cause issues in config files
|
285
|
-
alphabet = alphabet.replace('"', '').replace("'", '').replace('\\', '')
|
286
|
-
return ''.join(secrets.choice(alphabet) for _ in range(length))
|
287
13
|
|
288
|
-
|
289
|
-
|
290
|
-
|
14
|
+
class Command(CreateTokenCommand):
|
15
|
+
"""
|
16
|
+
Alias for create_token command.
|
291
17
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
tokens_dir = Path('tokens')
|
296
|
-
tokens_dir.mkdir(exist_ok=True)
|
297
|
-
|
298
|
-
# Create token file
|
299
|
-
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
300
|
-
filename = f"{token_type}_{name}_{timestamp}.txt"
|
301
|
-
filepath = tokens_dir / filename
|
302
|
-
|
303
|
-
with open(filepath, 'w') as f:
|
304
|
-
f.write(f"Token Type: {token_type}\n")
|
305
|
-
f.write(f"Name: {name}\n")
|
306
|
-
f.write(f"Token: {token}\n")
|
307
|
-
f.write(f"Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
308
|
-
if expiration_date:
|
309
|
-
f.write(f"Expires: {expiration_date.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
310
|
-
if user:
|
311
|
-
f.write(f"User: {user.username} ({user.email})\n")
|
312
|
-
f.write(f"\n# Add to your configuration:\n")
|
313
|
-
f.write(f"# {token_type.upper()}_KEY={token}\n")
|
314
|
-
|
315
|
-
self.stdout.write(f'💾 Token saved to: {filepath}')
|
316
|
-
|
317
|
-
# Also save to .env format
|
318
|
-
env_filename = f"{token_type}_{name}_{timestamp}.env"
|
319
|
-
env_filepath = tokens_dir / env_filename
|
320
|
-
|
321
|
-
with open(env_filepath, 'w') as f:
|
322
|
-
f.write(f"# {token_type.title()} - {name}\n")
|
323
|
-
f.write(f"# Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
324
|
-
if expiration_date:
|
325
|
-
f.write(f"# Expires: {expiration_date.strftime('%Y-%m-%d %H:%M:%S')}\n")
|
326
|
-
f.write(f"{token_type.upper()}_KEY={token}\n")
|
327
|
-
|
328
|
-
self.stdout.write(f'💾 Environment file saved to: {env_filepath}')
|
18
|
+
Simply inherits from CreateTokenCommand without any changes.
|
19
|
+
"""
|
20
|
+
pass
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""
|
2
|
+
Django-CFG wrapper for generate_client command.
|
3
|
+
|
4
|
+
This is a simple alias for django_client.management.commands.generate_client.
|
5
|
+
All logic is in django_client module.
|
6
|
+
|
7
|
+
Usage:
|
8
|
+
python manage.py generate_clients --groups blog shop
|
9
|
+
python manage.py generate_clients --python
|
10
|
+
python manage.py generate_clients --list-groups
|
11
|
+
"""
|
12
|
+
|
13
|
+
from django_cfg.modules.django_client.management.commands.generate_client import Command as DjangoClientCommand
|
14
|
+
|
15
|
+
|
16
|
+
class Command(DjangoClientCommand):
|
17
|
+
"""
|
18
|
+
Alias for generate_client command.
|
19
|
+
|
20
|
+
Simply inherits from DjangoClientCommand without any changes.
|
21
|
+
This allows both 'generate_client' and 'generate_clients' to work.
|
22
|
+
"""
|
23
|
+
pass
|
@@ -1,313 +1,20 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Django-CFG wrapper for list_urls command.
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
"""
|
7
|
-
|
8
|
-
import re
|
9
|
-
from django.core.management.base import BaseCommand
|
10
|
-
from django.urls import get_resolver
|
11
|
-
from django.conf import settings
|
12
|
-
from django_cfg.modules.django_logging import get_logger
|
13
|
-
|
14
|
-
|
15
|
-
# Rich imports for beautiful output
|
16
|
-
from rich.console import Console
|
17
|
-
from rich.panel import Panel
|
18
|
-
from rich.text import Text
|
19
|
-
from rich.table import Table
|
20
|
-
from rich.align import Align
|
21
|
-
|
22
|
-
from django_cfg.core.state import get_current_config
|
23
|
-
|
24
|
-
|
25
|
-
logger = get_logger('list_urls')
|
26
|
-
|
27
|
-
class Command(BaseCommand):
|
28
|
-
"""Command to display all available URLs in the project."""
|
29
|
-
|
30
|
-
# Web execution metadata
|
31
|
-
web_executable = True
|
32
|
-
requires_input = False
|
33
|
-
is_destructive = False
|
34
|
-
|
35
|
-
help = "Display all available URLs with Rich formatting"
|
36
|
-
|
37
|
-
def __init__(self, *args, **kwargs):
|
38
|
-
super().__init__(*args, **kwargs)
|
39
|
-
self.console = Console()
|
40
|
-
self.config = None
|
41
|
-
|
42
|
-
def add_arguments(self, parser):
|
43
|
-
parser.add_argument(
|
44
|
-
"--filter",
|
45
|
-
type=str,
|
46
|
-
help="Filter URLs containing this string",
|
47
|
-
default=None
|
48
|
-
)
|
49
|
-
parser.add_argument(
|
50
|
-
"--webhook",
|
51
|
-
action="store_true",
|
52
|
-
help="Show only webhook-related URLs"
|
53
|
-
)
|
54
|
-
parser.add_argument(
|
55
|
-
"--api",
|
56
|
-
action="store_true",
|
57
|
-
help="Show only API URLs"
|
58
|
-
)
|
59
|
-
parser.add_argument(
|
60
|
-
"--with-ngrok",
|
61
|
-
action="store_true",
|
62
|
-
help="Show ngrok URLs alongside local URLs"
|
63
|
-
)
|
64
|
-
|
65
|
-
def handle(self, *args, **options):
|
66
|
-
logger.info("Starting list_urls command")
|
67
|
-
filter_str = options["filter"]
|
68
|
-
webhook_only = options["webhook"]
|
69
|
-
api_only = options["api"]
|
70
|
-
with_ngrok = options["with_ngrok"]
|
71
|
-
|
72
|
-
# Show header
|
73
|
-
self.show_header()
|
74
|
-
|
75
|
-
# Load config
|
76
|
-
self.load_config()
|
4
|
+
This is a simple alias for django_admin.management.commands.list_urls.
|
5
|
+
All logic is in django_admin module.
|
77
6
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
# Filter URLs
|
82
|
-
if filter_str:
|
83
|
-
urls = [url for url in urls if filter_str.lower() in url['pattern'].lower()]
|
84
|
-
|
85
|
-
if webhook_only:
|
86
|
-
urls = [url for url in urls if 'webhook' in url['pattern'].lower() or 'hook' in url['pattern'].lower()]
|
87
|
-
|
88
|
-
if api_only:
|
89
|
-
urls = [url for url in urls if '/api/' in url['pattern'] or url['pattern'].startswith('api/')]
|
90
|
-
|
91
|
-
# Display URLs
|
92
|
-
self.display_urls(urls, with_ngrok)
|
93
|
-
|
94
|
-
# Show webhook info if requested
|
95
|
-
if webhook_only or with_ngrok:
|
96
|
-
self.show_webhook_info()
|
97
|
-
|
98
|
-
def show_header(self):
|
99
|
-
"""Show beautiful header with Rich."""
|
100
|
-
title = Text("Django URLs Overview", style="bold cyan")
|
101
|
-
subtitle = Text("All available URLs in your project", style="dim")
|
102
|
-
|
103
|
-
header_content = Align.center(
|
104
|
-
Text.assemble(
|
105
|
-
title, "\n",
|
106
|
-
subtitle
|
107
|
-
)
|
108
|
-
)
|
109
|
-
|
110
|
-
self.console.print()
|
111
|
-
self.console.print(Panel(
|
112
|
-
header_content,
|
113
|
-
title="🔗 URL Inspector",
|
114
|
-
border_style="bright_blue",
|
115
|
-
padding=(1, 2)
|
116
|
-
))
|
117
|
-
|
118
|
-
def load_config(self):
|
119
|
-
"""Load Django CFG configuration."""
|
120
|
-
try:
|
121
|
-
self.config = get_current_config()
|
122
|
-
except Exception as e:
|
123
|
-
self.console.print(f"[yellow]⚠️ Failed to load config: {e}[/yellow]")
|
124
|
-
self.config = None
|
125
|
-
|
126
|
-
def get_all_urls(self):
|
127
|
-
"""Extract all URLs from Django URL configuration."""
|
128
|
-
urls = []
|
129
|
-
resolver = get_resolver()
|
130
|
-
|
131
|
-
def extract_urls(url_patterns, prefix=''):
|
132
|
-
for pattern in url_patterns:
|
133
|
-
if hasattr(pattern, 'url_patterns'):
|
134
|
-
# This is an include() - recurse
|
135
|
-
new_prefix = prefix + str(pattern.pattern)
|
136
|
-
extract_urls(pattern.url_patterns, new_prefix)
|
137
|
-
else:
|
138
|
-
# This is a regular URL pattern
|
139
|
-
full_pattern = prefix + str(pattern.pattern)
|
140
|
-
|
141
|
-
# Clean up the pattern
|
142
|
-
clean_pattern = re.sub(r'\^|\$', '', full_pattern)
|
143
|
-
clean_pattern = re.sub(r'\\/', '/', clean_pattern)
|
144
|
-
|
145
|
-
# Get view info
|
146
|
-
view_name = getattr(pattern, 'name', None)
|
147
|
-
view_func = getattr(pattern, 'callback', None)
|
148
|
-
|
149
|
-
if view_func:
|
150
|
-
if hasattr(view_func, 'view_class'):
|
151
|
-
# Class-based view
|
152
|
-
view_info = f"{view_func.view_class.__name__}"
|
153
|
-
module = view_func.view_class.__module__
|
154
|
-
elif hasattr(view_func, '__name__'):
|
155
|
-
# Function-based view
|
156
|
-
view_info = f"{view_func.__name__}()"
|
157
|
-
module = getattr(view_func, '__module__', 'unknown')
|
158
|
-
else:
|
159
|
-
view_info = str(view_func)
|
160
|
-
module = 'unknown'
|
161
|
-
else:
|
162
|
-
view_info = 'Unknown'
|
163
|
-
module = 'unknown'
|
164
|
-
|
165
|
-
urls.append({
|
166
|
-
'pattern': clean_pattern,
|
167
|
-
'name': view_name,
|
168
|
-
'view': view_info,
|
169
|
-
'module': module
|
170
|
-
})
|
171
|
-
|
172
|
-
extract_urls(resolver.url_patterns)
|
173
|
-
return urls
|
174
|
-
|
175
|
-
def display_urls(self, urls, with_ngrok=False):
|
176
|
-
"""Display URLs in a Rich table."""
|
177
|
-
if not urls:
|
178
|
-
self.console.print("[yellow]No URLs found matching the criteria.[/yellow]")
|
179
|
-
return
|
180
|
-
|
181
|
-
# Create table
|
182
|
-
table = Table(title=f"🔗 Found {len(urls)} URLs", show_header=True, header_style="bold cyan")
|
183
|
-
table.add_column("URL Pattern", style="cyan", width=40)
|
184
|
-
table.add_column("Name", style="white", width=20)
|
185
|
-
table.add_column("View", style="green", width=25)
|
186
|
-
|
187
|
-
if with_ngrok:
|
188
|
-
table.add_column("Ngrok URL", style="magenta", width=40)
|
189
|
-
|
190
|
-
# Get base URLs
|
191
|
-
base_url = self.get_base_url()
|
192
|
-
ngrok_url = self.get_ngrok_url() if with_ngrok else None
|
193
|
-
|
194
|
-
# Add rows
|
195
|
-
for url in urls[:50]: # Limit to first 50 URLs
|
196
|
-
pattern = url['pattern']
|
197
|
-
name = url['name'] or '—'
|
198
|
-
view = url['view']
|
199
|
-
|
200
|
-
# Truncate long view names
|
201
|
-
if len(view) > 23:
|
202
|
-
view = view[:20] + "..."
|
203
|
-
|
204
|
-
row = [pattern, name, view]
|
205
|
-
|
206
|
-
if with_ngrok:
|
207
|
-
if ngrok_url:
|
208
|
-
full_ngrok_url = f"{ngrok_url.rstrip('/')}/{pattern.lstrip('/')}"
|
209
|
-
row.append(full_ngrok_url)
|
210
|
-
else:
|
211
|
-
row.append("—")
|
212
|
-
|
213
|
-
table.add_row(*row)
|
214
|
-
|
215
|
-
if len(urls) > 50:
|
216
|
-
table.caption = f"Showing first 50 of {len(urls)} URLs"
|
217
|
-
|
218
|
-
self.console.print(table)
|
7
|
+
Usage:
|
8
|
+
python manage.py list_urls
|
9
|
+
"""
|
219
10
|
|
220
|
-
|
221
|
-
info_table = Table(show_header=False, box=None)
|
222
|
-
info_table.add_column("Info", style="cyan")
|
223
|
-
info_table.add_column("Value", style="white")
|
224
|
-
|
225
|
-
info_table.add_row("🌐 Base URL:", base_url)
|
226
|
-
if with_ngrok and ngrok_url:
|
227
|
-
info_table.add_row("🔗 Ngrok URL:", ngrok_url)
|
228
|
-
|
229
|
-
self.console.print()
|
230
|
-
self.console.print(info_table)
|
11
|
+
from django_cfg.modules.django_admin.management.commands.list_urls import Command as ListUrlsCommand
|
231
12
|
|
232
|
-
def show_webhook_info(self):
|
233
|
-
"""Show webhook-specific information using reverse."""
|
234
|
-
self.console.print()
|
235
|
-
|
236
|
-
webhook_table = Table(title="🔔 Webhook Configuration", show_header=True, header_style="bold yellow")
|
237
|
-
webhook_table.add_column("Service", style="white", width=25)
|
238
|
-
webhook_table.add_column("Local URL", style="cyan", width=60)
|
239
|
-
webhook_table.add_column("Ngrok URL", style="magenta", width=60)
|
240
|
-
|
241
|
-
base_url = self.get_base_url()
|
242
|
-
ngrok_url = self.get_ngrok_url()
|
243
|
-
|
244
|
-
# Get webhook URLs using reverse
|
245
|
-
try:
|
246
|
-
from django.urls import reverse
|
247
|
-
|
248
|
-
# Common webhook endpoints with their URL names
|
249
|
-
webhooks = [
|
250
|
-
("Twilio Message Status", "cfg_accounts:webhook-message-status"),
|
251
|
-
("Twilio Verification", "cfg_accounts:webhook-verification-status"),
|
252
|
-
]
|
253
|
-
|
254
|
-
for service, url_name in webhooks:
|
255
|
-
try:
|
256
|
-
# Get the reversed URL path
|
257
|
-
url_path = reverse(url_name)
|
258
|
-
|
259
|
-
# Build full URLs
|
260
|
-
local_full = f"{base_url.rstrip('/')}{url_path}"
|
261
|
-
ngrok_full = f"{ngrok_url.rstrip('/')}{url_path}" if ngrok_url else "—"
|
262
|
-
|
263
|
-
webhook_table.add_row(service, local_full, ngrok_full)
|
264
|
-
|
265
|
-
except Exception as e:
|
266
|
-
# Fallback if reverse fails
|
267
|
-
self.console.print(f"[yellow]⚠️ Could not reverse URL for {service}: {e}[/yellow]")
|
268
|
-
fallback_path = f"/api/accounts/webhook/{service.lower().replace(' ', '-').replace('twilio ', '')}/"
|
269
|
-
local_full = f"{base_url.rstrip('/')}{fallback_path}"
|
270
|
-
ngrok_full = f"{ngrok_url.rstrip('/')}{fallback_path}" if ngrok_url else "—"
|
271
|
-
webhook_table.add_row(service, local_full, ngrok_full)
|
272
|
-
|
273
|
-
except ImportError:
|
274
|
-
# Fallback if Django is not available
|
275
|
-
webhooks = [
|
276
|
-
("Twilio Message Status", "/api/accounts/webhook/message-status/"),
|
277
|
-
("Twilio Verification", "/api/accounts/webhook/verification-status/"),
|
278
|
-
]
|
279
|
-
|
280
|
-
for service, endpoint in webhooks:
|
281
|
-
local_full = f"{base_url.rstrip('/')}{endpoint}"
|
282
|
-
ngrok_full = f"{ngrok_url.rstrip('/')}{endpoint}" if ngrok_url else "—"
|
283
|
-
webhook_table.add_row(service, local_full, ngrok_full)
|
284
|
-
|
285
|
-
self.console.print(webhook_table)
|
286
|
-
|
287
|
-
# Show tips
|
288
|
-
tips = [
|
289
|
-
"💡 Use ngrok URLs for webhook configuration in production services",
|
290
|
-
"🔒 Always validate webhook signatures in production",
|
291
|
-
"📝 Test webhooks using the test_twilio management command",
|
292
|
-
]
|
293
|
-
|
294
|
-
for tip in tips:
|
295
|
-
self.console.print(f"[dim]{tip}[/dim]")
|
296
13
|
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
return self.config.api_url
|
301
|
-
else:
|
302
|
-
# Fallback to Django settings
|
303
|
-
debug = getattr(settings, 'DEBUG', True)
|
304
|
-
if debug:
|
305
|
-
return "http://localhost:8000"
|
306
|
-
else:
|
307
|
-
return "https://yourdomain.com"
|
14
|
+
class Command(ListUrlsCommand):
|
15
|
+
"""
|
16
|
+
Alias for list_urls command.
|
308
17
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
return self.config.get_ngrok_url()
|
313
|
-
return None
|
18
|
+
Simply inherits from ListUrlsCommand without any changes.
|
19
|
+
"""
|
20
|
+
pass
|