django-cfg 1.4.10__py3-none-any.whl ā 1.4.13__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 +73 -49
- django_cfg/core/integration/display/startup.py +30 -22
- django_cfg/core/integration/url_integration.py +15 -16
- 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 +162 -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 +208 -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 +838 -0
- django_cfg/modules/django_client/core/generator/python/__init__.py +16 -0
- django_cfg/modules/django_client/core/generator/python/async_client_gen.py +174 -0
- django_cfg/modules/django_client/core/generator/python/files_generator.py +180 -0
- django_cfg/modules/django_client/core/generator/python/generator.py +182 -0
- django_cfg/modules/django_client/core/generator/python/models_generator.py +318 -0
- django_cfg/modules/django_client/core/generator/python/operations_generator.py +278 -0
- django_cfg/modules/django_client/core/generator/python/sync_client_gen.py +102 -0
- django_cfg/modules/django_client/core/generator/python/templates/__init__.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +153 -0
- django_cfg/modules/django_client/core/generator/python/templates/app_init.py.jinja +6 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/app_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/flat_client.py.jinja +38 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/main_client.py.jinja +68 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/main_client_file.py.jinja +14 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/operation_method.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sub_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_main_client.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_operation_method.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_sub_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +52 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/app_models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/enum_class.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/enums.py.jinja +8 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/schema_class.py.jinja +21 -0
- django_cfg/modules/django_client/core/generator/python/templates/pyproject.toml.jinja +55 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/logger.py.jinja +255 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/retry.py.jinja +271 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/schema.py.jinja +12 -0
- django_cfg/modules/django_client/core/generator/typescript/__init__.py +14 -0
- django_cfg/modules/django_client/core/generator/typescript/client_generator.py +165 -0
- django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +428 -0
- django_cfg/modules/django_client/core/generator/typescript/files_generator.py +207 -0
- django_cfg/modules/django_client/core/generator/typescript/generator.py +432 -0
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +536 -0
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py +245 -0
- django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +298 -0
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +329 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/api_instance.ts.jinja +131 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/app_index.ts.jinja +2 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/app_client.ts.jinja +18 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/client.ts.jinja +403 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/flat_client.ts.jinja +109 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/main_client_file.ts.jinja +10 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/operation.ts.jinja +61 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/sub_client.ts.jinja +15 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +45 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/index.ts.jinja +30 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/index.ts.jinja +5 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +268 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/app_models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/enums.ts.jinja +4 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/package.json.jinja +52 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/schemas/index.ts.jinja +21 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/schemas/schema.ts.jinja +24 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/tsconfig.json.jinja +20 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/errors.ts.jinja +116 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/http.ts.jinja +98 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/logger.ts.jinja +259 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/retry.ts.jinja +175 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/schema.ts.jinja +7 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/storage.ts.jinja +158 -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 +427 -0
- django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
- django_cfg/modules/django_client/pytest.ini +30 -0
- django_cfg/modules/django_client/spectacular/__init__.py +10 -0
- django_cfg/modules/django_client/spectacular/async_detection.py +187 -0
- django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
- django_cfg/modules/django_client/urls.py +72 -0
- django_cfg/{dashboard ā modules/django_dashboard}/DEBUG_README.md +2 -2
- django_cfg/{dashboard ā modules/django_dashboard}/REFACTORING_SUMMARY.md +1 -1
- django_cfg/modules/django_dashboard/management/__init__.py +0 -0
- django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
- django_cfg/{dashboard ā modules/django_dashboard}/management/commands/debug_dashboard.py +5 -5
- django_cfg/modules/django_dashboard/sections/documentation.py +391 -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/LOGGING_GUIDE.md +1 -1
- 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 +21 -10
- 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.13.dist-info}/METADATA +2 -2
- {django_cfg-1.4.10.dist-info ā django_cfg-1.4.13.dist-info}/RECORD +224 -74
- django_cfg/management/commands/generate.py +0 -107
- /django_cfg/models/django/{revolution.py ā revolution_legacy.py} +0 -0
- /django_cfg/{dashboard ā modules/django_admin}/management/__init__.py +0 -0
- /django_cfg/{dashboard ā modules/django_admin}/management/commands/__init__.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/__init__.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/components.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/debug.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/sections/__init__.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/sections/base.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/sections/commands.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/sections/overview.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/sections/stats.py +0 -0
- /django_cfg/{dashboard ā modules/django_dashboard}/sections/system.py +0 -0
- {django_cfg-1.4.10.dist-info ā django_cfg-1.4.13.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.10.dist-info ā django_cfg-1.4.13.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.10.dist-info ā django_cfg-1.4.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,313 @@
|
|
1
|
+
"""
|
2
|
+
Show URLs Command
|
3
|
+
|
4
|
+
Display all available URLs in the Django project with Rich formatting.
|
5
|
+
Useful for development and webhook configuration.
|
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()
|
77
|
+
|
78
|
+
# Get all URLs
|
79
|
+
urls = self.get_all_urls()
|
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)
|
219
|
+
|
220
|
+
# Show base URL info
|
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)
|
231
|
+
|
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
|
+
|
297
|
+
def get_base_url(self):
|
298
|
+
"""Get base URL for the application."""
|
299
|
+
if self.config:
|
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"
|
308
|
+
|
309
|
+
def get_ngrok_url(self):
|
310
|
+
"""Get ngrok URL if available."""
|
311
|
+
if self.config:
|
312
|
+
return self.config.get_ngrok_url()
|
313
|
+
return None
|
@@ -0,0 +1,133 @@
|
|
1
|
+
"""
|
2
|
+
Simple Migration Command for Django Config Toolkit
|
3
|
+
Migrate all databases based on django-cfg configuration.
|
4
|
+
"""
|
5
|
+
|
6
|
+
from django.core.management.base import BaseCommand
|
7
|
+
from django.core.management import call_command
|
8
|
+
from django.apps import apps
|
9
|
+
from django_cfg.modules.django_logging import get_logger
|
10
|
+
|
11
|
+
|
12
|
+
from django_cfg.core.state import get_current_config
|
13
|
+
|
14
|
+
|
15
|
+
logger = get_logger('migrate_all')
|
16
|
+
|
17
|
+
class Command(BaseCommand):
|
18
|
+
# Web execution metadata
|
19
|
+
web_executable = False
|
20
|
+
requires_input = False
|
21
|
+
is_destructive = True
|
22
|
+
|
23
|
+
help = "Migrate all databases based on django-cfg configuration"
|
24
|
+
|
25
|
+
def add_arguments(self, parser):
|
26
|
+
parser.add_argument(
|
27
|
+
"--dry-run",
|
28
|
+
action="store_true",
|
29
|
+
help="Show what would be migrated without executing"
|
30
|
+
)
|
31
|
+
parser.add_argument(
|
32
|
+
"--skip-makemigrations",
|
33
|
+
action="store_true",
|
34
|
+
help="Skip makemigrations step"
|
35
|
+
)
|
36
|
+
|
37
|
+
def handle(self, *args, **options):
|
38
|
+
"""Run migrations for all configured databases."""
|
39
|
+
logger.info("Starting migrate_all command")
|
40
|
+
dry_run = options.get("dry_run", False)
|
41
|
+
skip_makemigrations = options.get("skip_makemigrations", False)
|
42
|
+
|
43
|
+
if dry_run:
|
44
|
+
self.stdout.write(self.style.WARNING("š DRY RUN - No changes will be made"))
|
45
|
+
|
46
|
+
self.stdout.write(self.style.SUCCESS("š Migrating all databases..."))
|
47
|
+
|
48
|
+
# Step 1: Create migrations if needed
|
49
|
+
if not skip_makemigrations:
|
50
|
+
self.stdout.write("š Creating migrations...")
|
51
|
+
if not dry_run:
|
52
|
+
call_command("makemigrations", verbosity=1)
|
53
|
+
else:
|
54
|
+
self.stdout.write(" Would run: makemigrations")
|
55
|
+
|
56
|
+
# Step 2: Get database configuration
|
57
|
+
try:
|
58
|
+
config = get_current_config()
|
59
|
+
if not config or not hasattr(config, 'databases'):
|
60
|
+
self.stdout.write(self.style.ERROR("ā No django-cfg configuration found"))
|
61
|
+
return
|
62
|
+
except Exception as e:
|
63
|
+
self.stdout.write(self.style.ERROR(f"ā Error loading configuration: {e}"))
|
64
|
+
return
|
65
|
+
|
66
|
+
# Step 3: Migrate each database
|
67
|
+
for db_name, db_config in config.databases.items():
|
68
|
+
self.stdout.write(f"\nš Migrating database: {db_name}")
|
69
|
+
|
70
|
+
if hasattr(db_config, 'apps') and db_config.apps:
|
71
|
+
# Migrate specific apps for this database
|
72
|
+
for app_path in db_config.apps:
|
73
|
+
app_label = self._get_app_label(app_path)
|
74
|
+
if app_label:
|
75
|
+
self.stdout.write(f" š¦ Migrating {app_label}...")
|
76
|
+
if not dry_run:
|
77
|
+
try:
|
78
|
+
call_command("migrate", app_label, database=db_name, verbosity=1)
|
79
|
+
except Exception as e:
|
80
|
+
self.stdout.write(self.style.ERROR(f" ā Migration failed for {app_label} on {db_name}: {e}"))
|
81
|
+
logger.error(f"Migration failed for {app_label} on {db_name}: {e}")
|
82
|
+
raise SystemExit(1)
|
83
|
+
else:
|
84
|
+
self.stdout.write(f" Would run: migrate {app_label} --database={db_name}")
|
85
|
+
else:
|
86
|
+
# Migrate all apps for this database (usually default)
|
87
|
+
self.stdout.write(f" š¦ Migrating all apps...")
|
88
|
+
if not dry_run:
|
89
|
+
try:
|
90
|
+
call_command("migrate", database=db_name, verbosity=1)
|
91
|
+
except Exception as e:
|
92
|
+
self.stdout.write(self.style.ERROR(f" ā Migration failed for all apps on {db_name}: {e}"))
|
93
|
+
logger.error(f"Migration failed for all apps on {db_name}: {e}")
|
94
|
+
raise SystemExit(1)
|
95
|
+
else:
|
96
|
+
self.stdout.write(f" Would run: migrate --database={db_name}")
|
97
|
+
|
98
|
+
# Step 4: Migrate constance if needed
|
99
|
+
self.stdout.write(f"\nš§ Migrating constance...")
|
100
|
+
if not dry_run:
|
101
|
+
try:
|
102
|
+
call_command("migrate", "constance", database="default", verbosity=1)
|
103
|
+
except Exception as e:
|
104
|
+
self.stdout.write(self.style.ERROR(f"ā Constance migration failed: {e}"))
|
105
|
+
logger.error(f"Constance migration failed: {e}")
|
106
|
+
raise SystemExit(1)
|
107
|
+
else:
|
108
|
+
self.stdout.write(" Would run: migrate constance --database=default")
|
109
|
+
|
110
|
+
self.stdout.write(self.style.SUCCESS("\nā
All migrations completed!"))
|
111
|
+
|
112
|
+
def _get_app_label(self, app_path: str) -> str:
|
113
|
+
"""Convert full module path to Django app_label."""
|
114
|
+
try:
|
115
|
+
# Try to get app config by full path first
|
116
|
+
try:
|
117
|
+
app_config = apps.get_app_config(app_path)
|
118
|
+
return app_config.label
|
119
|
+
except LookupError:
|
120
|
+
pass
|
121
|
+
|
122
|
+
# Fallback: extract last part of the path as potential app_label
|
123
|
+
potential_label = app_path.split('.')[-1]
|
124
|
+
try:
|
125
|
+
app_config = apps.get_app_config(potential_label)
|
126
|
+
return app_config.label
|
127
|
+
except LookupError:
|
128
|
+
pass
|
129
|
+
|
130
|
+
return app_path
|
131
|
+
|
132
|
+
except Exception:
|
133
|
+
return app_path
|