django-cfg 1.3.13__py3-none-any.whl → 1.4.0__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/__init__.py +1 -1
- django_cfg/apps/accounts/admin/user_admin.py +39 -16
- django_cfg/apps/accounts/serializers/profile.py +1 -1
- django_cfg/apps/accounts/services/otp_service.py +18 -11
- django_cfg/apps/accounts/signals.py +15 -24
- django_cfg/apps/accounts/utils/notifications.py +217 -358
- django_cfg/apps/accounts/views/otp.py +2 -2
- django_cfg/apps/accounts/views/webhook.py +1 -1
- django_cfg/apps/agents/core/django_agent.py +1 -1
- django_cfg/apps/api/commands/views.py +66 -83
- django_cfg/apps/api/health/drf_views.py +269 -0
- django_cfg/apps/api/health/serializers.py +45 -0
- django_cfg/apps/api/health/urls.py +6 -1
- django_cfg/apps/knowbase/admin/actions/__init__.py +13 -0
- django_cfg/apps/knowbase/admin/actions/visibility_actions.py +56 -0
- django_cfg/apps/knowbase/admin/document_admin.py +136 -270
- django_cfg/apps/knowbase/admin/helpers/__init__.py +17 -0
- django_cfg/apps/knowbase/admin/helpers/configs.py +72 -0
- django_cfg/apps/knowbase/admin/helpers/display_helpers.py +156 -0
- django_cfg/apps/knowbase/admin/helpers/statistics.py +108 -0
- django_cfg/apps/knowbase/config/constance_fields.py +1 -1
- django_cfg/apps/knowbase/config/settings.py +2 -2
- django_cfg/apps/knowbase/mixins/__init__.py +19 -2
- django_cfg/apps/knowbase/mixins/config/__init__.py +14 -0
- django_cfg/apps/knowbase/mixins/config/defaults.py +75 -0
- django_cfg/apps/knowbase/mixins/config/meta_config.py +120 -0
- django_cfg/apps/knowbase/mixins/creator.py +10 -10
- django_cfg/apps/knowbase/mixins/external_data_mixin.py +105 -403
- django_cfg/apps/knowbase/mixins/generators/__init__.py +16 -0
- django_cfg/apps/knowbase/mixins/generators/content_generator.py +218 -0
- django_cfg/apps/knowbase/mixins/generators/field_analyzer.py +76 -0
- django_cfg/apps/knowbase/mixins/generators/metadata_generator.py +124 -0
- django_cfg/apps/knowbase/mixins/service.py +2 -2
- django_cfg/apps/knowbase/services/archive/__init__.py +1 -0
- django_cfg/apps/knowbase/services/archive/analyzers/__init__.py +17 -0
- django_cfg/apps/knowbase/services/archive/analyzers/complexity_analyzer.py +33 -0
- django_cfg/apps/knowbase/services/archive/analyzers/purpose_detector.py +36 -0
- django_cfg/apps/knowbase/services/archive/analyzers/quality_analyzer.py +39 -0
- django_cfg/apps/knowbase/services/archive/analyzers/tag_generator.py +103 -0
- django_cfg/apps/knowbase/services/archive/chunking/__init__.py +19 -0
- django_cfg/apps/knowbase/services/archive/chunking/base.py +81 -0
- django_cfg/apps/knowbase/services/archive/chunking/json_chunker.py +62 -0
- django_cfg/apps/knowbase/services/archive/chunking/markdown_chunker.py +107 -0
- django_cfg/apps/knowbase/services/archive/chunking/python_chunker.py +248 -0
- django_cfg/apps/knowbase/services/archive/chunking/text_chunker.py +70 -0
- django_cfg/apps/knowbase/services/archive/chunking_service.py +110 -729
- django_cfg/apps/knowbase/services/archive/context/__init__.py +14 -0
- django_cfg/apps/knowbase/services/archive/context/builders.py +220 -0
- django_cfg/apps/knowbase/services/archive/context/models.py +38 -0
- django_cfg/apps/knowbase/services/embedding/models.py +18 -14
- django_cfg/apps/knowbase/services/embedding/processors.py +6 -3
- django_cfg/apps/knowbase/tasks/document_processing.py +11 -3
- django_cfg/apps/leads/tests.py +1 -1
- django_cfg/apps/payments/admin/api_keys_admin.py +1 -1
- django_cfg/apps/payments/admin/balance_admin.py +1 -1
- django_cfg/apps/payments/admin/currencies_admin.py +1 -1
- django_cfg/apps/payments/admin/payments_admin.py +1 -1
- django_cfg/apps/payments/admin/subscriptions_admin.py +1 -1
- django_cfg/apps/payments/admin_interface/templates/payments/base.html +59 -126
- django_cfg/apps/payments/admin_interface/views/api/payments.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/stats.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/users.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/webhook_admin.py +1 -1
- django_cfg/apps/payments/admin_interface/views/api/webhook_public.py +1 -1
- django_cfg/apps/payments/admin_interface/views/base.py +29 -2
- django_cfg/apps/payments/apps.py +1 -1
- django_cfg/apps/payments/config/django_cfg_integration.py +2 -2
- django_cfg/apps/payments/config/helpers.py +3 -2
- django_cfg/apps/payments/management/commands/cleanup_expired_data.py +1 -1
- django_cfg/apps/payments/management/commands/currency_stats.py +1 -1
- django_cfg/apps/payments/management/commands/manage_currencies.py +1 -1
- django_cfg/apps/payments/management/commands/manage_providers.py +1 -1
- django_cfg/apps/payments/management/commands/process_pending_payments.py +1 -1
- django_cfg/apps/payments/management/commands/test_providers.py +1 -1
- django_cfg/apps/payments/middleware/api_access.py +1 -1
- django_cfg/apps/payments/middleware/rate_limiting.py +1 -1
- django_cfg/apps/payments/middleware/usage_tracking.py +1 -1
- django_cfg/apps/payments/models/balance.py +2 -2
- django_cfg/apps/payments/models/managers/api_key_managers.py +1 -1
- django_cfg/apps/payments/models/managers/balance_managers.py +1 -1
- django_cfg/apps/payments/models/managers/currency_managers.py +1 -1
- django_cfg/apps/payments/models/managers/payment_managers.py +1 -1
- django_cfg/apps/payments/models/managers/subscription_managers.py +1 -1
- django_cfg/apps/payments/models/payments.py +2 -2
- django_cfg/apps/payments/services/cache_service/__init__.py +1 -1
- django_cfg/apps/payments/services/cache_service/simple_cache.py +10 -5
- django_cfg/apps/payments/services/core/base.py +1 -1
- django_cfg/apps/payments/services/core/currency/__init__.py +13 -0
- django_cfg/apps/payments/services/core/currency/currency_converter.py +57 -0
- django_cfg/apps/payments/services/core/currency/currency_validator.py +61 -0
- django_cfg/apps/payments/services/core/operations/__init__.py +15 -0
- django_cfg/apps/payments/services/core/operations/payment_canceller.py +100 -0
- django_cfg/apps/payments/services/core/operations/payment_creator.py +196 -0
- django_cfg/apps/payments/services/core/operations/status_checker.py +100 -0
- django_cfg/apps/payments/services/core/payment_service.py +124 -612
- django_cfg/apps/payments/services/core/providers/__init__.py +13 -0
- django_cfg/apps/payments/services/core/providers/provider_client.py +132 -0
- django_cfg/apps/payments/services/core/providers/status_mapper.py +89 -0
- django_cfg/apps/payments/services/core/utils/__init__.py +13 -0
- django_cfg/apps/payments/services/core/utils/data_converter.py +48 -0
- django_cfg/apps/payments/services/core/utils/statistics_calculator.py +69 -0
- django_cfg/apps/payments/services/providers/base.py +1 -1
- django_cfg/apps/payments/services/providers/nowpayments/__init__.py +3 -3
- django_cfg/apps/payments/services/providers/nowpayments/parsers/__init__.py +9 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/__init__.py +23 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/constants.py +23 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/currency_names.py +244 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/data/patterns.py +511 -0
- django_cfg/apps/payments/services/providers/nowpayments/parsers/parser.py +168 -0
- django_cfg/apps/payments/services/providers/nowpayments/provider.py +1 -1
- django_cfg/apps/payments/services/providers/nowpayments/sync.py +1 -1
- django_cfg/apps/payments/services/providers/registry.py +1 -1
- django_cfg/apps/payments/services/providers/sync_service.py +1 -1
- django_cfg/apps/payments/signals/__init__.py +1 -1
- django_cfg/apps/payments/signals/api_key_signals.py +1 -1
- django_cfg/apps/payments/signals/balance_signals.py +1 -1
- django_cfg/apps/payments/signals/payment_signals.py +1 -1
- django_cfg/apps/payments/signals/subscription_signals.py +1 -1
- django_cfg/apps/payments/views/api/api_keys.py +1 -1
- django_cfg/apps/payments/views/api/balances.py +1 -1
- django_cfg/apps/payments/views/api/base.py +1 -1
- django_cfg/apps/payments/views/api/currencies.py +1 -1
- django_cfg/apps/payments/views/api/payments.py +1 -1
- django_cfg/apps/payments/views/api/subscriptions.py +1 -1
- django_cfg/apps/payments/views/api/webhooks.py +1 -1
- django_cfg/apps/payments/views/serializers/api_keys.py +1 -1
- django_cfg/apps/payments/views/serializers/balances.py +1 -1
- django_cfg/apps/payments/views/serializers/currencies.py +1 -1
- django_cfg/apps/payments/views/serializers/payments.py +1 -1
- django_cfg/apps/payments/views/serializers/subscriptions.py +1 -1
- django_cfg/apps/payments/views/serializers/webhooks.py +1 -1
- django_cfg/apps/support/admin/support_admin.py +21 -13
- django_cfg/apps/support/templates/support/chat/access_denied.html +21 -27
- django_cfg/apps/support/templates/support/chat/ticket_chat.html +183 -254
- django_cfg/apps/support/utils/support_email_service.py +1 -1
- django_cfg/apps/tasks/templates/tasks/layout/base.html +20 -115
- django_cfg/apps/tasks/utils/simulator.py +1 -1
- django_cfg/apps/tasks/views/dashboard.py +33 -3
- django_cfg/apps/urls.py +5 -1
- django_cfg/cli/README.md +57 -471
- django_cfg/cli/commands/create_project.py +140 -529
- django_cfg/cli/main.py +13 -10
- django_cfg/core/__init__.py +63 -6
- django_cfg/core/base/__init__.py +5 -0
- django_cfg/core/base/config_model.py +652 -0
- django_cfg/core/builders/__init__.py +11 -0
- django_cfg/core/builders/apps_builder.py +258 -0
- django_cfg/core/builders/middleware_builder.py +115 -0
- django_cfg/core/builders/security_builder.py +96 -0
- django_cfg/core/config.py +20 -892
- django_cfg/core/constants.py +69 -0
- django_cfg/core/environment/__init__.py +9 -0
- django_cfg/core/exceptions.py +45 -298
- django_cfg/core/generation/__init__.py +51 -0
- django_cfg/core/generation/core_generators/__init__.py +0 -0
- django_cfg/core/generation/core_generators/settings.py +90 -0
- django_cfg/core/generation/core_generators/static.py +82 -0
- django_cfg/core/generation/core_generators/templates.py +141 -0
- django_cfg/core/generation/data_generators/__init__.py +15 -0
- django_cfg/core/generation/data_generators/cache.py +132 -0
- django_cfg/core/generation/data_generators/database.py +117 -0
- django_cfg/core/generation/generation.py +92 -0
- django_cfg/core/generation/integration_generators/__init__.py +21 -0
- django_cfg/core/generation/integration_generators/api.py +237 -0
- django_cfg/core/generation/integration_generators/sessions.py +65 -0
- django_cfg/core/generation/integration_generators/tailwind.py +54 -0
- django_cfg/core/generation/integration_generators/tasks.py +92 -0
- django_cfg/core/generation/integration_generators/third_party.py +144 -0
- django_cfg/core/generation/orchestrator.py +285 -0
- django_cfg/core/generation/protocols.py +30 -0
- django_cfg/core/generation/security_generators/__init__.py +0 -0
- django_cfg/core/generation/utility_generators/__init__.py +24 -0
- django_cfg/core/generation/utility_generators/email.py +58 -0
- django_cfg/core/generation/utility_generators/i18n.py +66 -0
- django_cfg/core/generation/utility_generators/limits.py +58 -0
- django_cfg/core/generation/utility_generators/logging.py +66 -0
- django_cfg/core/generation/utility_generators/security.py +101 -0
- django_cfg/core/generation/utils/__init__.py +0 -0
- django_cfg/core/generation/utils/helpers.py +32 -0
- django_cfg/core/integration/__init__.py +18 -25
- django_cfg/core/integration/display/startup.py +146 -133
- django_cfg/core/integration/url_integration.py +13 -2
- django_cfg/core/services/__init__.py +5 -0
- django_cfg/core/services/config_service.py +121 -0
- django_cfg/core/state/__init__.py +9 -0
- django_cfg/core/state/registry.py +84 -0
- django_cfg/core/types/__init__.py +15 -0
- django_cfg/core/types/aliases.py +15 -0
- django_cfg/core/types/enums.py +49 -0
- django_cfg/dashboard/DEBUG_README.md +105 -0
- django_cfg/dashboard/REFACTORING_SUMMARY.md +237 -0
- django_cfg/dashboard/__init__.py +24 -0
- django_cfg/dashboard/components.py +308 -0
- django_cfg/dashboard/debug.py +176 -0
- django_cfg/dashboard/management/__init__.py +0 -0
- django_cfg/dashboard/management/commands/__init__.py +0 -0
- django_cfg/dashboard/management/commands/debug_dashboard.py +109 -0
- django_cfg/dashboard/sections/__init__.py +1 -0
- django_cfg/dashboard/sections/base.py +128 -0
- django_cfg/dashboard/sections/commands.py +32 -0
- django_cfg/dashboard/sections/overview.py +394 -0
- django_cfg/dashboard/sections/stats.py +48 -0
- django_cfg/dashboard/sections/system.py +73 -0
- django_cfg/management/commands/check_settings.py +6 -2
- django_cfg/management/commands/clear_constance.py +6 -1
- django_cfg/management/commands/create_token.py +5 -4
- django_cfg/management/commands/generate.py +5 -0
- django_cfg/management/commands/list_urls.py +7 -2
- django_cfg/management/commands/migrate_all.py +6 -2
- django_cfg/management/commands/migrator.py +6 -1
- django_cfg/management/commands/rundramatiq.py +6 -1
- django_cfg/management/commands/rundramatiq_simulator.py +11 -4
- django_cfg/management/commands/runserver_ngrok.py +9 -7
- django_cfg/management/commands/script.py +25 -21
- django_cfg/management/commands/show_config.py +6 -1
- django_cfg/management/commands/show_urls.py +8 -3
- django_cfg/management/commands/superuser.py +5 -4
- django_cfg/management/commands/task_clear.py +8 -3
- django_cfg/management/commands/task_status.py +8 -3
- django_cfg/management/commands/test_email.py +6 -1
- django_cfg/management/commands/test_telegram.py +6 -1
- django_cfg/management/commands/test_twilio.py +6 -1
- django_cfg/management/commands/tree.py +7 -4
- django_cfg/models/__init__.py +88 -3
- django_cfg/models/api/__init__.py +27 -0
- django_cfg/models/{api.py → api/config.py} +1 -1
- django_cfg/models/api/drf/__init__.py +21 -0
- django_cfg/models/api/drf/config.py +101 -0
- django_cfg/models/api/drf/redoc.py +31 -0
- django_cfg/models/api/drf/spectacular.py +129 -0
- django_cfg/models/api/drf/swagger.py +59 -0
- django_cfg/models/{api_keys.py → api/keys.py} +16 -6
- django_cfg/models/{limits.py → api/limits.py} +0 -1
- django_cfg/models/base/__init__.py +14 -0
- django_cfg/models/django/__init__.py +16 -0
- django_cfg/models/{constance.py → django/constance.py} +1 -1
- django_cfg/models/{environment.py → django/environment.py} +1 -1
- django_cfg/models/infrastructure/__init__.py +17 -0
- django_cfg/models/{cache.py → infrastructure/cache.py} +3 -2
- django_cfg/models/infrastructure/database/__init__.py +22 -0
- django_cfg/models/infrastructure/database/config.py +265 -0
- django_cfg/models/infrastructure/database/converters.py +91 -0
- django_cfg/models/infrastructure/database/parsers.py +96 -0
- django_cfg/models/infrastructure/database/routing.py +85 -0
- django_cfg/models/infrastructure/database/validators.py +170 -0
- django_cfg/models/{logging.py → infrastructure/logging.py} +1 -1
- django_cfg/models/{security.py → infrastructure/security.py} +2 -2
- django_cfg/models/ngrok/__init__.py +11 -0
- django_cfg/models/ngrok/auth.py +37 -0
- django_cfg/models/ngrok/config.py +77 -0
- django_cfg/models/ngrok/tunnel.py +35 -0
- django_cfg/models/payments/__init__.py +20 -0
- django_cfg/models/payments/api_keys.py +57 -0
- django_cfg/models/{payments.py → payments/config.py} +56 -154
- django_cfg/models/payments/providers/__init__.py +15 -0
- django_cfg/models/payments/providers/base.py +25 -0
- django_cfg/models/payments/providers/nowpayments.py +48 -0
- django_cfg/models/services/__init__.py +18 -0
- django_cfg/models/services/base.py +65 -0
- django_cfg/models/{email.py → services/email.py} +1 -1
- django_cfg/models/services/telegram.py +172 -0
- django_cfg/models/tasks/__init__.py +51 -0
- django_cfg/models/tasks/backends.py +250 -0
- django_cfg/models/tasks/config.py +314 -0
- django_cfg/models/tasks/utils.py +174 -0
- django_cfg/modules/base.py +18 -3
- django_cfg/modules/django_admin/decorators/actions.py +1 -1
- django_cfg/modules/django_admin/decorators/display.py +1 -1
- django_cfg/modules/django_admin/mixins/standalone_actions_mixin.py +1 -1
- django_cfg/modules/django_cfg_rpc_client/README.md +346 -0
- django_cfg/modules/django_cfg_rpc_client/__init__.py +51 -0
- django_cfg/modules/django_cfg_rpc_client/client.py +540 -0
- django_cfg/modules/django_cfg_rpc_client/config.py +207 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/README.md +517 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/__init__.py +11 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/apps.py +22 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/monitor.py +435 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/static/django_cfg_rpc_dashboard/js/dashboard.js +373 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/base.html +76 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/templates/django_cfg_rpc_dashboard/dashboard.html +200 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/urls.py +22 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/urls_admin.py +9 -0
- django_cfg/modules/django_cfg_rpc_client/dashboard/views.py +251 -0
- django_cfg/modules/django_cfg_rpc_client/exceptions.py +201 -0
- django_cfg/modules/django_drf_theme/CHANGELOG.md +210 -0
- django_cfg/modules/django_drf_theme/EXAMPLE.md +465 -0
- django_cfg/modules/django_drf_theme/IMPLEMENTATION.md +232 -0
- django_cfg/modules/django_drf_theme/README.md +207 -0
- django_cfg/modules/django_drf_theme/TAILWIND_CDN_GUIDE.md +274 -0
- django_cfg/modules/django_drf_theme/__init__.py +23 -0
- django_cfg/modules/django_drf_theme/apps.py +15 -0
- django_cfg/modules/django_drf_theme/renderers.py +58 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/api.html +375 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/base.html +938 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/filter_form.html +132 -0
- django_cfg/modules/django_drf_theme/templates/rest_framework/tailwind/forms/raw_data_form.html +123 -0
- django_cfg/modules/django_drf_theme/templatetags/__init__.py +1 -0
- django_cfg/modules/django_drf_theme/templatetags/tailwind_tags.py +57 -0
- django_cfg/modules/django_email/__init__.py +14 -0
- django_cfg/modules/{django_email.py → django_email/service.py} +78 -113
- django_cfg/modules/django_email/utils.py +40 -0
- django_cfg/modules/django_health/__init__.py +9 -0
- django_cfg/modules/{django_health.py → django_health/service.py} +23 -21
- django_cfg/modules/django_llm/llm/client.py +155 -550
- django_cfg/modules/django_llm/llm/embeddings/__init__.py +13 -0
- django_cfg/modules/django_llm/llm/embeddings/mock_embedder.py +106 -0
- django_cfg/modules/django_llm/llm/embeddings/openai_embedder.py +79 -0
- django_cfg/modules/django_llm/llm/models_api/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/models_api/models_query.py +163 -0
- django_cfg/modules/django_llm/llm/providers/__init__.py +15 -0
- django_cfg/modules/django_llm/llm/providers/config_builder.py +103 -0
- django_cfg/modules/django_llm/llm/providers/provider_manager.py +148 -0
- django_cfg/modules/django_llm/llm/providers/provider_selector.py +60 -0
- django_cfg/modules/django_llm/llm/requests/__init__.py +15 -0
- django_cfg/modules/django_llm/llm/requests/cache_manager.py +170 -0
- django_cfg/modules/django_llm/llm/requests/chat_handler.py +199 -0
- django_cfg/modules/django_llm/llm/requests/embedding_handler.py +113 -0
- django_cfg/modules/django_llm/llm/responses/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/responses/response_builder.py +131 -0
- django_cfg/modules/django_llm/llm/stats/__init__.py +9 -0
- django_cfg/modules/django_llm/llm/stats/stats_manager.py +107 -0
- django_cfg/modules/django_llm/translator/detectors/__init__.py +13 -0
- django_cfg/modules/django_llm/translator/detectors/language_detector.py +90 -0
- django_cfg/modules/django_llm/translator/detectors/script_detector.py +153 -0
- django_cfg/modules/django_llm/translator/stats/__init__.py +11 -0
- django_cfg/modules/django_llm/translator/stats/stats_tracker.py +85 -0
- django_cfg/modules/django_llm/translator/translator.py +150 -603
- django_cfg/modules/django_llm/translator/translators/__init__.py +15 -0
- django_cfg/modules/django_llm/translator/translators/json_translator.py +316 -0
- django_cfg/modules/django_llm/translator/translators/text_translator.py +139 -0
- django_cfg/modules/django_llm/translator/utils/__init__.py +13 -0
- django_cfg/modules/django_llm/translator/utils/prompt_builder.py +110 -0
- django_cfg/modules/django_llm/translator/utils/text_utils.py +114 -0
- django_cfg/modules/django_logging/FIXES_SUMMARY.md +276 -0
- django_cfg/modules/django_logging/LOGGING_GUIDE.md +504 -0
- django_cfg/modules/django_logging/__init__.py +14 -0
- django_cfg/modules/{django_logger.py → django_logging/django_logger.py} +13 -13
- django_cfg/modules/{logger.py → django_logging/logger.py} +14 -4
- django_cfg/modules/django_ngrok/__init__.py +39 -0
- django_cfg/modules/{django_ngrok.py → django_ngrok/service.py} +14 -42
- django_cfg/modules/django_rpc_old/POETRY.md +344 -0
- django_cfg/modules/django_rpc_old/README.md +397 -0
- django_cfg/modules/django_rpc_old/TESTING.md +358 -0
- django_cfg/modules/django_rpc_old/__init__.py +39 -0
- django_cfg/modules/django_rpc_old/client.py +531 -0
- django_cfg/modules/django_rpc_old/config.py +279 -0
- django_cfg/modules/django_rpc_old/exceptions.py +172 -0
- django_cfg/modules/django_tailwind/README.md +478 -0
- django_cfg/modules/django_tailwind/__init__.py +7 -0
- django_cfg/modules/django_tailwind/apps.py +10 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/app.html +5 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +117 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/navbar.html +124 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/theme_toggle.html +54 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/components/user_menu.html +116 -0
- django_cfg/modules/django_tailwind/templates/django_tailwind/simple.html +46 -0
- django_cfg/modules/django_tailwind/templatetags/__init__.py +1 -0
- django_cfg/modules/django_tailwind/templatetags/tailwind_info.py +185 -0
- django_cfg/modules/django_tasks/__init__.py +29 -0
- django_cfg/modules/django_tasks/factory.py +127 -0
- django_cfg/modules/{django_tasks.py → django_tasks/service.py} +45 -274
- django_cfg/modules/django_tasks/settings.py +107 -0
- django_cfg/modules/django_telegram/__init__.py +29 -0
- django_cfg/modules/{django_telegram.py → django_telegram/service.py} +45 -113
- django_cfg/modules/django_telegram/utils.py +62 -0
- django_cfg/modules/django_twilio/__init__.py +54 -107
- django_cfg/modules/django_twilio/_imports.py +30 -0
- django_cfg/modules/django_twilio/base.py +192 -0
- django_cfg/modules/django_twilio/email_otp.py +227 -0
- django_cfg/modules/django_twilio/sendgrid_service.py +1 -1
- django_cfg/modules/django_twilio/simple_service.py +1 -2
- django_cfg/modules/django_twilio/sms.py +94 -0
- django_cfg/modules/django_twilio/twilio_service.py +2 -3
- django_cfg/modules/django_twilio/unified.py +310 -0
- django_cfg/modules/django_twilio/utils.py +190 -0
- django_cfg/modules/django_twilio/whatsapp.py +137 -0
- django_cfg/modules/django_unfold/callbacks/base.py +198 -7
- django_cfg/modules/django_unfold/callbacks/main.py +102 -10
- django_cfg/modules/django_unfold/dashboard.py +65 -43
- django_cfg/modules/django_unfold/models/config.py +13 -12
- django_cfg/modules/django_unfold/models/navigation.py +8 -3
- django_cfg/modules/django_unfold/models/tabs.py +2 -2
- django_cfg/modules/django_unfold/templates/unfold/helpers/app_list.html +102 -0
- django_cfg/registry/core.py +24 -26
- django_cfg/registry/modules.py +5 -2
- django_cfg/registry/services.py +20 -3
- django_cfg/registry/third_party.py +8 -8
- django_cfg/static/admin/css/dashboard.css +260 -0
- django_cfg/static/admin/js/commands.js +171 -0
- django_cfg/static/admin/js/dashboard.js +126 -0
- django_cfg/templates/admin/components/management_commands.js +375 -0
- django_cfg/templates/admin/components/progress_bar.html +18 -23
- django_cfg/templates/admin/index.html +48 -20
- django_cfg/templates/admin/index_new.html +106 -0
- django_cfg/templates/admin/layouts/base_dashboard.html +60 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +1 -20
- django_cfg/templates/admin/sections/commands_section.html +626 -0
- django_cfg/templates/admin/sections/overview_section.html +112 -0
- django_cfg/templates/admin/sections/stats_section.html +35 -0
- django_cfg/templates/admin/sections/system_section.html +99 -0
- django_cfg/templates/admin/snippets/components/CHARTS_GUIDE.md +322 -0
- django_cfg/templates/admin/snippets/components/activity_tracker.html +85 -47
- django_cfg/templates/admin/snippets/components/charts_section.html +154 -64
- django_cfg/templates/admin/snippets/components/django_commands.html +3 -3
- django_cfg/templates/admin/snippets/components/recent_activity_improved.html +25 -0
- django_cfg/templates/admin/snippets/components/recent_users_table.html +1 -1
- django_cfg/templates/admin/snippets/components/system_metrics.html +179 -93
- django_cfg/templates/admin/snippets/zones/zones_table.html +2 -2
- django_cfg/templatetags/django_cfg.py +7 -1
- django_cfg/utils/smart_defaults.py +4 -4
- django_cfg-1.4.0.dist-info/METADATA +920 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/RECORD +424 -195
- django_cfg/apps/accounts/utils/auth_email_service.py +0 -84
- django_cfg/apps/payments/services/providers/nowpayments/parsers.py +0 -879
- django_cfg/core/generation.py +0 -621
- django_cfg/management/commands/validate_config.py +0 -189
- django_cfg/models/database.py +0 -480
- django_cfg/models/drf.py +0 -272
- django_cfg/models/ngrok.py +0 -122
- django_cfg/models/services.py +0 -440
- django_cfg/models/tasks.py +0 -550
- django_cfg/modules/django_twilio/service.py +0 -942
- django_cfg/template_archive/django_sample.zip +0 -0
- django_cfg/templates/rest_framework/api.html +0 -12
- django_cfg/utils/toolkit.py +0 -703
- django_cfg-1.3.13.dist-info/METADATA +0 -1029
- /django_cfg/apps/accounts/management/commands/{test_otp.py → otp_test.py} +0 -0
- /django_cfg/core/{environment.py → environment/detector.py} +0 -0
- /django_cfg/models/{cors.py → api/cors.py} +0 -0
- /django_cfg/models/{jwt.py → api/jwt.py} +0 -0
- /django_cfg/models/{base.py → base/config.py} +0 -0
- /django_cfg/models/{cfg.py → base/module.py} +0 -0
- /django_cfg/models/{revolution.py → django/revolution.py} +0 -0
- /django_cfg/modules/{dramatiq_setup.py → django_tasks/dramatiq_setup.py} +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,265 @@
|
|
1
|
+
"""
|
2
|
+
Database configuration model for django_cfg.
|
3
|
+
|
4
|
+
Type-safe database connection configuration with validation.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, List, Optional, Any, Literal
|
8
|
+
from pydantic import BaseModel, Field, field_validator, model_validator, PrivateAttr
|
9
|
+
|
10
|
+
from . import validators
|
11
|
+
from . import converters
|
12
|
+
from . import routing
|
13
|
+
from .parsers import detect_engine_from_url
|
14
|
+
|
15
|
+
|
16
|
+
class DatabaseConfig(BaseModel):
|
17
|
+
"""
|
18
|
+
Type-safe database connection configuration.
|
19
|
+
|
20
|
+
Supports both individual connection parameters and connection strings.
|
21
|
+
Automatically validates connection parameters and provides helpful error messages.
|
22
|
+
"""
|
23
|
+
|
24
|
+
model_config = {
|
25
|
+
"str_strip_whitespace": True,
|
26
|
+
"validate_assignment": True,
|
27
|
+
"extra": "forbid", # Prevent typos in field names
|
28
|
+
}
|
29
|
+
|
30
|
+
# Core connection parameters
|
31
|
+
engine: Optional[str] = Field(
|
32
|
+
default=None,
|
33
|
+
description="Django database engine (e.g., 'django.db.backends.postgresql'). If not provided, will be auto-detected from database URL scheme.",
|
34
|
+
min_length=1,
|
35
|
+
)
|
36
|
+
|
37
|
+
name: str = Field(
|
38
|
+
...,
|
39
|
+
description="Database name or connection string",
|
40
|
+
min_length=1,
|
41
|
+
)
|
42
|
+
|
43
|
+
user: Optional[str] = Field(
|
44
|
+
default=None,
|
45
|
+
description="Database username",
|
46
|
+
)
|
47
|
+
|
48
|
+
password: Optional[str] = Field(
|
49
|
+
default=None,
|
50
|
+
description="Database password",
|
51
|
+
repr=False, # Don't show in repr for security
|
52
|
+
)
|
53
|
+
|
54
|
+
host: str = Field(
|
55
|
+
default="localhost",
|
56
|
+
description="Database host",
|
57
|
+
min_length=1,
|
58
|
+
)
|
59
|
+
|
60
|
+
port: int = Field(
|
61
|
+
default=5432,
|
62
|
+
description="Database port",
|
63
|
+
ge=1,
|
64
|
+
le=65535,
|
65
|
+
)
|
66
|
+
|
67
|
+
# Connection options
|
68
|
+
connect_timeout: int = Field(
|
69
|
+
default=10,
|
70
|
+
description="Connection timeout in seconds",
|
71
|
+
ge=1,
|
72
|
+
le=300, # Max 5 minutes
|
73
|
+
)
|
74
|
+
|
75
|
+
sslmode: str = Field(
|
76
|
+
default="prefer",
|
77
|
+
description="SSL mode for connection",
|
78
|
+
)
|
79
|
+
|
80
|
+
# Additional database options
|
81
|
+
options: Dict[str, Any] = Field(
|
82
|
+
default_factory=dict,
|
83
|
+
description="Additional database-specific options",
|
84
|
+
)
|
85
|
+
|
86
|
+
# Database routing configuration
|
87
|
+
apps: List[str] = Field(
|
88
|
+
default_factory=list,
|
89
|
+
description="Django app labels that should use this database",
|
90
|
+
)
|
91
|
+
|
92
|
+
operations: List[Literal["read", "write", "migrate"]] = Field(
|
93
|
+
default_factory=lambda: ["read", "write", "migrate"],
|
94
|
+
description="Allowed operations for this database",
|
95
|
+
min_length=1,
|
96
|
+
)
|
97
|
+
|
98
|
+
migrate_to: Optional[str] = Field(
|
99
|
+
default=None,
|
100
|
+
description="Override database alias for migrations (if different from this database)",
|
101
|
+
)
|
102
|
+
|
103
|
+
routing_description: str = Field(
|
104
|
+
default="",
|
105
|
+
description="Human-readable description of the routing rule",
|
106
|
+
)
|
107
|
+
|
108
|
+
# Internal fields for parsed connection strings
|
109
|
+
_is_connection_string: bool = PrivateAttr(default=False)
|
110
|
+
_parsed_components: Optional[Dict[str, Any]] = PrivateAttr(default=None)
|
111
|
+
|
112
|
+
# Validators
|
113
|
+
@field_validator("engine")
|
114
|
+
@classmethod
|
115
|
+
def validate_engine(cls, v: Optional[str]) -> Optional[str]:
|
116
|
+
"""Validate Django database engine format."""
|
117
|
+
return validators.validate_engine(v)
|
118
|
+
|
119
|
+
@field_validator("name")
|
120
|
+
@classmethod
|
121
|
+
def validate_name(cls, v: str) -> str:
|
122
|
+
"""Validate database name or parse connection string."""
|
123
|
+
return validators.validate_name(v)
|
124
|
+
|
125
|
+
@field_validator("sslmode")
|
126
|
+
@classmethod
|
127
|
+
def validate_sslmode(cls, v: str) -> str:
|
128
|
+
"""Validate SSL mode values."""
|
129
|
+
return validators.validate_sslmode(v)
|
130
|
+
|
131
|
+
@field_validator("apps")
|
132
|
+
@classmethod
|
133
|
+
def validate_apps(cls, v: List[str]) -> List[str]:
|
134
|
+
"""Validate app labels format."""
|
135
|
+
return validators.validate_apps(v)
|
136
|
+
|
137
|
+
@field_validator("operations")
|
138
|
+
@classmethod
|
139
|
+
def validate_operations(cls, v: List[str]) -> List[str]:
|
140
|
+
"""Validate operations list."""
|
141
|
+
return validators.validate_operations(v)
|
142
|
+
|
143
|
+
@model_validator(mode="before")
|
144
|
+
@classmethod
|
145
|
+
def validate_connection_consistency(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
146
|
+
"""Validate connection parameter consistency and auto-detect engine."""
|
147
|
+
return validators.validate_connection_consistency(values)
|
148
|
+
|
149
|
+
@model_validator(mode="after")
|
150
|
+
def validate_connection_after(self) -> "DatabaseConfig":
|
151
|
+
"""Validate connection after model creation."""
|
152
|
+
return validators.validate_connection_after(self)
|
153
|
+
|
154
|
+
# Converters
|
155
|
+
def to_django_config(self) -> Dict[str, Any]:
|
156
|
+
"""
|
157
|
+
Convert to Django database configuration format.
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
Django-compatible database configuration dictionary
|
161
|
+
|
162
|
+
Raises:
|
163
|
+
DatabaseError: If configuration cannot be converted
|
164
|
+
"""
|
165
|
+
return converters.to_django_config(self)
|
166
|
+
|
167
|
+
# Routing methods
|
168
|
+
def matches_app(self, app_label: str) -> bool:
|
169
|
+
"""
|
170
|
+
Check if this database should be used for the given app.
|
171
|
+
|
172
|
+
Args:
|
173
|
+
app_label: Django app label to check
|
174
|
+
|
175
|
+
Returns:
|
176
|
+
True if this database should be used for the app
|
177
|
+
"""
|
178
|
+
return routing.matches_app(self, app_label)
|
179
|
+
|
180
|
+
def allows_operation(self, operation: str) -> bool:
|
181
|
+
"""
|
182
|
+
Check if this database allows the given operation.
|
183
|
+
|
184
|
+
Args:
|
185
|
+
operation: Operation to check ('read', 'write', 'migrate')
|
186
|
+
|
187
|
+
Returns:
|
188
|
+
True if operation is allowed
|
189
|
+
"""
|
190
|
+
return routing.allows_operation(self, operation)
|
191
|
+
|
192
|
+
def get_migration_database(self) -> Optional[str]:
|
193
|
+
"""
|
194
|
+
Get the database alias to use for migrations.
|
195
|
+
|
196
|
+
Returns:
|
197
|
+
Database alias for migrations, or None to use this database
|
198
|
+
"""
|
199
|
+
return routing.get_migration_database(self)
|
200
|
+
|
201
|
+
def has_routing_rules(self) -> bool:
|
202
|
+
"""
|
203
|
+
Check if this database has any routing rules configured.
|
204
|
+
|
205
|
+
Returns:
|
206
|
+
True if apps list is not empty
|
207
|
+
"""
|
208
|
+
return routing.has_routing_rules(self)
|
209
|
+
|
210
|
+
def test_connection(self) -> bool:
|
211
|
+
"""
|
212
|
+
Test database connection (placeholder for future implementation).
|
213
|
+
|
214
|
+
Returns:
|
215
|
+
True if connection successful, False otherwise
|
216
|
+
"""
|
217
|
+
return routing.test_connection(self)
|
218
|
+
|
219
|
+
# Factory methods
|
220
|
+
@classmethod
|
221
|
+
def from_url(
|
222
|
+
cls,
|
223
|
+
url: str,
|
224
|
+
*,
|
225
|
+
apps: Optional[List[str]] = None,
|
226
|
+
operations: Optional[List[Literal["read", "write", "migrate"]]] = None,
|
227
|
+
routing_description: str = "",
|
228
|
+
**kwargs
|
229
|
+
) -> "DatabaseConfig":
|
230
|
+
"""
|
231
|
+
Create DatabaseConfig from URL with automatic engine detection.
|
232
|
+
|
233
|
+
Args:
|
234
|
+
url: Database URL (e.g., 'postgresql://user:pass@host:port/db')
|
235
|
+
apps: Django app labels that should use this database
|
236
|
+
operations: Allowed operations for this database
|
237
|
+
routing_description: Human-readable description of the routing rule
|
238
|
+
**kwargs: Additional parameters to override defaults
|
239
|
+
|
240
|
+
Returns:
|
241
|
+
DatabaseConfig instance with auto-detected engine
|
242
|
+
|
243
|
+
Example:
|
244
|
+
# Simple SQLite database
|
245
|
+
db = DatabaseConfig.from_url("sqlite:///db.sqlite3")
|
246
|
+
|
247
|
+
# PostgreSQL with routing
|
248
|
+
blog_db = DatabaseConfig.from_url(
|
249
|
+
"postgresql://user:pass@localhost:5432/blog",
|
250
|
+
apps=["apps.blog"],
|
251
|
+
routing_description="Blog posts and comments"
|
252
|
+
)
|
253
|
+
"""
|
254
|
+
return cls(
|
255
|
+
name=url,
|
256
|
+
apps=apps or [],
|
257
|
+
operations=operations or ["read", "write", "migrate"],
|
258
|
+
routing_description=routing_description,
|
259
|
+
**kwargs
|
260
|
+
)
|
261
|
+
|
262
|
+
|
263
|
+
__all__ = [
|
264
|
+
"DatabaseConfig",
|
265
|
+
]
|
@@ -0,0 +1,91 @@
|
|
1
|
+
"""
|
2
|
+
Database configuration converters.
|
3
|
+
|
4
|
+
Convert DatabaseConfig to Django settings format.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, Any
|
8
|
+
|
9
|
+
|
10
|
+
def to_django_config(config: "DatabaseConfig") -> Dict[str, Any]: # type: ignore
|
11
|
+
"""
|
12
|
+
Convert DatabaseConfig to Django database configuration format.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
config: DatabaseConfig instance
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
Django-compatible database configuration dictionary
|
19
|
+
|
20
|
+
Raises:
|
21
|
+
DatabaseError: If configuration cannot be converted
|
22
|
+
"""
|
23
|
+
try:
|
24
|
+
# Base configuration
|
25
|
+
django_config = {
|
26
|
+
"ENGINE": config.engine,
|
27
|
+
"OPTIONS": {**config.options},
|
28
|
+
}
|
29
|
+
|
30
|
+
# Add database-specific options
|
31
|
+
if config.engine == "django.db.backends.postgresql":
|
32
|
+
# PostgreSQL supports connect_timeout and sslmode
|
33
|
+
django_config["OPTIONS"]["connect_timeout"] = config.connect_timeout
|
34
|
+
django_config["OPTIONS"]["sslmode"] = config.sslmode
|
35
|
+
elif config.engine == "django.db.backends.mysql":
|
36
|
+
# MySQL supports connect_timeout but not sslmode
|
37
|
+
django_config["OPTIONS"]["connect_timeout"] = config.connect_timeout
|
38
|
+
# SQLite doesn't support connect_timeout or sslmode, so we skip them
|
39
|
+
|
40
|
+
# Handle connection string vs individual parameters
|
41
|
+
if config._is_connection_string:
|
42
|
+
# For connection strings, use the full string as NAME
|
43
|
+
django_config["NAME"] = config.name
|
44
|
+
|
45
|
+
# Add parsed components if available
|
46
|
+
if config._parsed_components:
|
47
|
+
parsed = config._parsed_components
|
48
|
+
if parsed.get("database"):
|
49
|
+
django_config["NAME"] = parsed["database"]
|
50
|
+
if parsed.get("user"):
|
51
|
+
django_config["USER"] = parsed["user"]
|
52
|
+
if parsed.get("password"):
|
53
|
+
django_config["PASSWORD"] = parsed["password"]
|
54
|
+
if parsed.get("host"):
|
55
|
+
django_config["HOST"] = parsed["host"]
|
56
|
+
if parsed.get("port"):
|
57
|
+
django_config["PORT"] = parsed["port"]
|
58
|
+
if parsed.get("options"):
|
59
|
+
django_config["OPTIONS"].update(parsed["options"])
|
60
|
+
else:
|
61
|
+
# Individual parameters
|
62
|
+
django_config["NAME"] = config.name
|
63
|
+
|
64
|
+
if config.user:
|
65
|
+
django_config["USER"] = config.user
|
66
|
+
|
67
|
+
if config.password:
|
68
|
+
django_config["PASSWORD"] = config.password
|
69
|
+
|
70
|
+
if config.host:
|
71
|
+
django_config["HOST"] = config.host
|
72
|
+
|
73
|
+
if config.port:
|
74
|
+
django_config["PORT"] = config.port
|
75
|
+
|
76
|
+
return django_config
|
77
|
+
|
78
|
+
except Exception as e:
|
79
|
+
# Import here to avoid circular dependency
|
80
|
+
from django_cfg.core.exceptions import DatabaseError
|
81
|
+
|
82
|
+
raise DatabaseError(
|
83
|
+
f"Failed to convert database configuration: {e}",
|
84
|
+
database_alias=getattr(config, "_alias", "unknown"),
|
85
|
+
context={"config": config.model_dump()}
|
86
|
+
) from e
|
87
|
+
|
88
|
+
|
89
|
+
__all__ = [
|
90
|
+
"to_django_config",
|
91
|
+
]
|
@@ -0,0 +1,96 @@
|
|
1
|
+
"""
|
2
|
+
Database connection string parsers.
|
3
|
+
|
4
|
+
Utilities for parsing and detecting database engines from URLs.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, Any
|
8
|
+
from urllib.parse import urlparse
|
9
|
+
|
10
|
+
|
11
|
+
def detect_engine_from_url(url: str) -> str:
|
12
|
+
"""
|
13
|
+
Automatically detect Django database engine from URL scheme.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
url: Database URL (e.g., 'postgresql://...', 'sqlite:///...')
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
Django database engine string
|
20
|
+
|
21
|
+
Raises:
|
22
|
+
ValueError: If URL scheme is not supported
|
23
|
+
"""
|
24
|
+
if "://" not in url:
|
25
|
+
# Assume SQLite for file paths without scheme
|
26
|
+
return "django.db.backends.sqlite3"
|
27
|
+
|
28
|
+
scheme = url.split("://")[0].lower()
|
29
|
+
|
30
|
+
# Map URL schemes to Django engines
|
31
|
+
scheme_to_engine = {
|
32
|
+
"postgresql": "django.db.backends.postgresql",
|
33
|
+
"postgres": "django.db.backends.postgresql",
|
34
|
+
"mysql": "django.db.backends.mysql",
|
35
|
+
"sqlite": "django.db.backends.sqlite3",
|
36
|
+
"sqlite3": "django.db.backends.sqlite3",
|
37
|
+
"oracle": "django.db.backends.oracle",
|
38
|
+
}
|
39
|
+
|
40
|
+
if scheme in scheme_to_engine:
|
41
|
+
return scheme_to_engine[scheme]
|
42
|
+
|
43
|
+
raise ValueError(
|
44
|
+
f"Unsupported database scheme '{scheme}'. "
|
45
|
+
f"Supported schemes: {', '.join(scheme_to_engine.keys())}"
|
46
|
+
)
|
47
|
+
|
48
|
+
|
49
|
+
def parse_connection_string(connection_string: str) -> Dict[str, Any]:
|
50
|
+
"""
|
51
|
+
Parse database connection string into components.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
connection_string: Database URL to parse
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
Dictionary with parsed components (scheme, user, password, host, port, database, options)
|
58
|
+
|
59
|
+
Raises:
|
60
|
+
DatabaseError: If connection string cannot be parsed
|
61
|
+
"""
|
62
|
+
try:
|
63
|
+
parsed = urlparse(connection_string)
|
64
|
+
|
65
|
+
components = {
|
66
|
+
"scheme": parsed.scheme,
|
67
|
+
"user": parsed.username,
|
68
|
+
"password": parsed.password,
|
69
|
+
"host": parsed.hostname,
|
70
|
+
"port": parsed.port,
|
71
|
+
"database": parsed.path.lstrip("/") if parsed.path else None,
|
72
|
+
}
|
73
|
+
|
74
|
+
# Parse query parameters as options
|
75
|
+
if parsed.query:
|
76
|
+
from urllib.parse import parse_qs
|
77
|
+
|
78
|
+
query_params = parse_qs(parsed.query)
|
79
|
+
components["options"] = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()}
|
80
|
+
|
81
|
+
return components
|
82
|
+
|
83
|
+
except Exception as e:
|
84
|
+
# Import here to avoid circular dependency
|
85
|
+
from django_cfg.core.exceptions import DatabaseError
|
86
|
+
|
87
|
+
raise DatabaseError(
|
88
|
+
f"Failed to parse connection string: {e}",
|
89
|
+
context={"connection_string": connection_string}
|
90
|
+
) from e
|
91
|
+
|
92
|
+
|
93
|
+
__all__ = [
|
94
|
+
"detect_engine_from_url",
|
95
|
+
"parse_connection_string",
|
96
|
+
]
|
@@ -0,0 +1,85 @@
|
|
1
|
+
"""
|
2
|
+
Database routing utilities.
|
3
|
+
|
4
|
+
Methods for database routing and operation checks.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Optional
|
8
|
+
|
9
|
+
|
10
|
+
def matches_app(config: "DatabaseConfig", app_label: str) -> bool: # type: ignore
|
11
|
+
"""
|
12
|
+
Check if this database should be used for the given app.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
config: DatabaseConfig instance
|
16
|
+
app_label: Django app label to check
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
True if this database should be used for the app
|
20
|
+
"""
|
21
|
+
return app_label in config.apps
|
22
|
+
|
23
|
+
|
24
|
+
def allows_operation(config: "DatabaseConfig", operation: str) -> bool: # type: ignore
|
25
|
+
"""
|
26
|
+
Check if this database allows the given operation.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
config: DatabaseConfig instance
|
30
|
+
operation: Operation to check ('read', 'write', 'migrate')
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
True if operation is allowed
|
34
|
+
"""
|
35
|
+
return operation in config.operations
|
36
|
+
|
37
|
+
|
38
|
+
def get_migration_database(config: "DatabaseConfig") -> Optional[str]: # type: ignore
|
39
|
+
"""
|
40
|
+
Get the database alias to use for migrations.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
config: DatabaseConfig instance
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
Database alias for migrations, or None to use this database
|
47
|
+
"""
|
48
|
+
return config.migrate_to
|
49
|
+
|
50
|
+
|
51
|
+
def has_routing_rules(config: "DatabaseConfig") -> bool: # type: ignore
|
52
|
+
"""
|
53
|
+
Check if this database has any routing rules configured.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
config: DatabaseConfig instance
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
True if apps list is not empty
|
60
|
+
"""
|
61
|
+
return bool(config.apps)
|
62
|
+
|
63
|
+
|
64
|
+
def test_connection(config: "DatabaseConfig") -> bool: # type: ignore
|
65
|
+
"""
|
66
|
+
Test database connection (placeholder for future implementation).
|
67
|
+
|
68
|
+
Args:
|
69
|
+
config: DatabaseConfig instance
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
True if connection successful, False otherwise
|
73
|
+
"""
|
74
|
+
# TODO: Implement actual connection testing
|
75
|
+
# This would require Django to be available and configured
|
76
|
+
return True
|
77
|
+
|
78
|
+
|
79
|
+
__all__ = [
|
80
|
+
"matches_app",
|
81
|
+
"allows_operation",
|
82
|
+
"get_migration_database",
|
83
|
+
"has_routing_rules",
|
84
|
+
"test_connection",
|
85
|
+
]
|
@@ -0,0 +1,170 @@
|
|
1
|
+
"""
|
2
|
+
Database configuration validators.
|
3
|
+
|
4
|
+
Field validators for DatabaseConfig model.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Dict, List, Any, Optional
|
8
|
+
from pathlib import Path
|
9
|
+
from urllib.parse import urlparse
|
10
|
+
|
11
|
+
from .parsers import detect_engine_from_url, parse_connection_string
|
12
|
+
|
13
|
+
|
14
|
+
def validate_engine(v: Optional[str]) -> Optional[str]:
|
15
|
+
"""Validate Django database engine format."""
|
16
|
+
if v is None:
|
17
|
+
return v
|
18
|
+
|
19
|
+
if not v.startswith("django.db.backends."):
|
20
|
+
raise ValueError(
|
21
|
+
f"Invalid database engine '{v}'. "
|
22
|
+
"Must start with 'django.db.backends.'"
|
23
|
+
)
|
24
|
+
|
25
|
+
# Common engines validation
|
26
|
+
valid_engines = {
|
27
|
+
"django.db.backends.postgresql",
|
28
|
+
"django.db.backends.mysql",
|
29
|
+
"django.db.backends.sqlite3",
|
30
|
+
"django.db.backends.oracle",
|
31
|
+
}
|
32
|
+
|
33
|
+
if v not in valid_engines and not v.startswith("django.db.backends."):
|
34
|
+
# Allow custom backends but warn about common typos
|
35
|
+
common_typos = {
|
36
|
+
"postgresql": "django.db.backends.postgresql",
|
37
|
+
"postgres": "django.db.backends.postgresql",
|
38
|
+
"mysql": "django.db.backends.mysql",
|
39
|
+
"sqlite": "django.db.backends.sqlite3",
|
40
|
+
"sqlite3": "django.db.backends.sqlite3",
|
41
|
+
}
|
42
|
+
|
43
|
+
if v in common_typos:
|
44
|
+
raise ValueError(
|
45
|
+
f"Invalid engine '{v}'. Did you mean '{common_typos[v]}'?"
|
46
|
+
)
|
47
|
+
|
48
|
+
return v
|
49
|
+
|
50
|
+
|
51
|
+
def validate_name(v: str) -> str:
|
52
|
+
"""Validate database name or parse connection string."""
|
53
|
+
# Check if it's a connection string
|
54
|
+
if "://" in v:
|
55
|
+
try:
|
56
|
+
parsed = urlparse(v)
|
57
|
+
if not parsed.scheme:
|
58
|
+
raise ValueError("Invalid connection string format")
|
59
|
+
return v
|
60
|
+
except Exception as e:
|
61
|
+
raise ValueError(f"Invalid connection string: {e}") from e
|
62
|
+
|
63
|
+
# Regular database name validation
|
64
|
+
if v in [":memory:", ""]:
|
65
|
+
return v # Special cases for SQLite
|
66
|
+
|
67
|
+
# Check for path-like names (SQLite files)
|
68
|
+
if "/" in v or "\\" in v or v.endswith(".db") or v.endswith(".sqlite3"):
|
69
|
+
path = Path(v)
|
70
|
+
if path.is_absolute() or v.startswith("./") or v.startswith("../"):
|
71
|
+
return v # Valid file path
|
72
|
+
|
73
|
+
return v
|
74
|
+
|
75
|
+
|
76
|
+
def validate_sslmode(v: str) -> str:
|
77
|
+
"""Validate SSL mode values."""
|
78
|
+
valid_modes = {"disable", "allow", "prefer", "require", "verify-ca", "verify-full"}
|
79
|
+
|
80
|
+
if v not in valid_modes:
|
81
|
+
raise ValueError(
|
82
|
+
f"Invalid SSL mode '{v}'. "
|
83
|
+
f"Valid options: {', '.join(sorted(valid_modes))}"
|
84
|
+
)
|
85
|
+
|
86
|
+
return v
|
87
|
+
|
88
|
+
|
89
|
+
def validate_connection_consistency(values: Dict[str, Any]) -> Dict[str, Any]:
|
90
|
+
"""Validate connection parameter consistency and auto-detect engine."""
|
91
|
+
# Auto-detect engine if not provided
|
92
|
+
if values.get("engine") is None and values.get("name"):
|
93
|
+
values["engine"] = detect_engine_from_url(values["name"])
|
94
|
+
|
95
|
+
return values
|
96
|
+
|
97
|
+
|
98
|
+
def validate_connection_after(config: "DatabaseConfig") -> "DatabaseConfig": # type: ignore
|
99
|
+
"""Validate connection after model creation."""
|
100
|
+
# Parse connection string if present
|
101
|
+
if "://" in config.name:
|
102
|
+
object.__setattr__(config, '_is_connection_string', True)
|
103
|
+
parsed = parse_connection_string(config.name)
|
104
|
+
object.__setattr__(config, '_parsed_components', parsed)
|
105
|
+
|
106
|
+
# Override individual parameters with parsed values if not explicitly set
|
107
|
+
if parsed and not config.user and parsed.get("user"):
|
108
|
+
object.__setattr__(config, 'user', parsed["user"])
|
109
|
+
if parsed and not config.password and parsed.get("password"):
|
110
|
+
object.__setattr__(config, 'password', parsed["password"])
|
111
|
+
if parsed and config.host == "localhost" and parsed.get("host"):
|
112
|
+
object.__setattr__(config, 'host', parsed["host"])
|
113
|
+
if parsed and config.port == 5432 and parsed.get("port"):
|
114
|
+
object.__setattr__(config, 'port', parsed["port"])
|
115
|
+
|
116
|
+
# Validate SQLite-specific constraints
|
117
|
+
if config.engine == "django.db.backends.sqlite3":
|
118
|
+
if config.name not in [":memory:", ""] and not (
|
119
|
+
config.name.endswith((".db", ".sqlite", ".sqlite3"))
|
120
|
+
or "/" in config.name
|
121
|
+
or "\\" in config.name
|
122
|
+
):
|
123
|
+
raise ValueError(
|
124
|
+
"SQLite database name must be ':memory:', a file path, "
|
125
|
+
"or end with .db, .sqlite, or .sqlite3"
|
126
|
+
)
|
127
|
+
|
128
|
+
# Validate PostgreSQL-specific constraints
|
129
|
+
elif config.engine == "django.db.backends.postgresql":
|
130
|
+
if not config._is_connection_string and not config.name:
|
131
|
+
raise ValueError("PostgreSQL database name is required")
|
132
|
+
|
133
|
+
return config
|
134
|
+
|
135
|
+
|
136
|
+
def validate_apps(v: List[str]) -> List[str]:
|
137
|
+
"""Validate app labels format."""
|
138
|
+
for app in v:
|
139
|
+
if not app or not app.replace("_", "").replace(".", "").isalnum():
|
140
|
+
raise ValueError(
|
141
|
+
f"Invalid app label '{app}'. "
|
142
|
+
"App labels must contain only letters, numbers, dots, and underscores"
|
143
|
+
)
|
144
|
+
return v
|
145
|
+
|
146
|
+
|
147
|
+
def validate_operations(v: List[str]) -> List[str]:
|
148
|
+
"""Validate operations list."""
|
149
|
+
if not v:
|
150
|
+
raise ValueError("At least one operation must be specified")
|
151
|
+
|
152
|
+
valid_ops = {"read", "write", "migrate"}
|
153
|
+
for op in v:
|
154
|
+
if op not in valid_ops:
|
155
|
+
raise ValueError(
|
156
|
+
f"Invalid operation '{op}'. "
|
157
|
+
f"Valid operations: {', '.join(sorted(valid_ops))}"
|
158
|
+
)
|
159
|
+
return v
|
160
|
+
|
161
|
+
|
162
|
+
__all__ = [
|
163
|
+
"validate_engine",
|
164
|
+
"validate_name",
|
165
|
+
"validate_sslmode",
|
166
|
+
"validate_connection_consistency",
|
167
|
+
"validate_connection_after",
|
168
|
+
"validate_apps",
|
169
|
+
"validate_operations",
|
170
|
+
]
|