django-cfg 1.3.13__py3-none-any.whl → 1.4.3__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/agents/examples/__init__.py +3 -0
- django_cfg/apps/agents/examples/simple_example.py +161 -0
- 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/examples/__init__.py +3 -0
- django_cfg/apps/knowbase/examples/external_data_usage.py +191 -0
- 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/examples/vehicle_model_example.py +199 -0
- 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_currency/examples/__init__.py +3 -0
- django_cfg/modules/django_currency/examples/example_database_usage.py +144 -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_ipc_client/README.md +346 -0
- django_cfg/modules/django_ipc_client/__init__.py +51 -0
- django_cfg/modules/django_ipc_client/client.py +540 -0
- django_cfg/modules/django_ipc_client/config.py +207 -0
- django_cfg/modules/django_ipc_client/dashboard/README.md +517 -0
- django_cfg/modules/django_ipc_client/dashboard/UNFOLD_INTEGRATION.md +439 -0
- django_cfg/modules/django_ipc_client/dashboard/__init__.py +11 -0
- django_cfg/modules/django_ipc_client/dashboard/apps.py +22 -0
- django_cfg/modules/django_ipc_client/dashboard/monitor.py +435 -0
- django_cfg/modules/django_ipc_client/dashboard/static/django_ipc_dashboard/js/dashboard.js +373 -0
- django_cfg/modules/django_ipc_client/dashboard/templates/django_ipc_dashboard/base.html +76 -0
- django_cfg/modules/django_ipc_client/dashboard/templates/django_ipc_dashboard/dashboard.html +200 -0
- django_cfg/modules/django_ipc_client/dashboard/urls.py +22 -0
- django_cfg/modules/django_ipc_client/dashboard/urls_admin.py +9 -0
- django_cfg/modules/django_ipc_client/dashboard/views.py +251 -0
- django_cfg/modules/django_ipc_client/exceptions.py +201 -0
- 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/examples/component_class_example.html +156 -0
- 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.3.dist-info/METADATA +533 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/RECORD +432 -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.3.dist-info}/WHEEL +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.3.13.dist-info → django_cfg-1.4.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,189 +0,0 @@
|
|
1
|
-
# """
|
2
|
-
# Django management command to validate configuration.
|
3
|
-
|
4
|
-
# Usage:
|
5
|
-
# python manage.py validate_config
|
6
|
-
# """
|
7
|
-
|
8
|
-
# from django.core.management.base import BaseCommand, CommandError
|
9
|
-
# from django.conf import settings
|
10
|
-
# from django.core.cache import cache
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# class Command(BaseCommand):
|
15
|
-
# help = 'Validate Django Config Toolkit configuration'
|
16
|
-
|
17
|
-
# def add_arguments(self, parser):
|
18
|
-
# parser.add_argument(
|
19
|
-
# '--show-details',
|
20
|
-
# action='store_true',
|
21
|
-
# help='Show detailed configuration information',
|
22
|
-
# )
|
23
|
-
# parser.add_argument(
|
24
|
-
# '--check-connections',
|
25
|
-
# action='store_true',
|
26
|
-
# help='Test database and cache connections',
|
27
|
-
# )
|
28
|
-
|
29
|
-
# def handle(self, *args, **options):
|
30
|
-
# """Validate configuration and optionally show details."""
|
31
|
-
# self.stdout.write(
|
32
|
-
# self.style.HTTP_INFO('🚀 Django Config Toolkit - Configuration Validation')
|
33
|
-
# )
|
34
|
-
# self.stdout.write('=' * 60)
|
35
|
-
|
36
|
-
# try:
|
37
|
-
# # Initialize toolkit
|
38
|
-
# toolkit = ConfigToolkit()
|
39
|
-
|
40
|
-
# # Basic validation
|
41
|
-
# self.stdout.write(
|
42
|
-
# self.style.SUCCESS(f'✅ Configuration loaded successfully')
|
43
|
-
# )
|
44
|
-
# self.stdout.write(f' Environment: {toolkit.environment}')
|
45
|
-
# self.stdout.write(f' Debug: {toolkit.debug}')
|
46
|
-
# self.stdout.write(f' Configs loaded: {toolkit._config_count}')
|
47
|
-
# self.stdout.write(f' Init time: {toolkit._init_time_ms:.2f}ms')
|
48
|
-
|
49
|
-
# # Check secret key
|
50
|
-
# if len(toolkit.secret_key) >= 50:
|
51
|
-
# self.stdout.write(self.style.SUCCESS('✅ Secret key is secure'))
|
52
|
-
# else:
|
53
|
-
# self.stdout.write(
|
54
|
-
# self.style.WARNING('⚠️ Secret key is too short (< 50 chars)')
|
55
|
-
# )
|
56
|
-
|
57
|
-
# # Check extended features
|
58
|
-
# self.stdout.write('\n🎨 Extended Features:')
|
59
|
-
# features = [
|
60
|
-
# ('Unfold Admin', toolkit.unfold_enabled),
|
61
|
-
# ('Revolution API', toolkit.revolution_enabled),
|
62
|
-
# ('Constance Settings', toolkit.constance_enabled),
|
63
|
-
# ('Advanced Logging', toolkit.logging_enabled),
|
64
|
-
# ]
|
65
|
-
|
66
|
-
# for name, enabled in features:
|
67
|
-
# status = '✅' if enabled else '❌'
|
68
|
-
# self.stdout.write(f' {status} {name}')
|
69
|
-
|
70
|
-
# # Detailed information
|
71
|
-
# if options['show_details']:
|
72
|
-
# self._show_detailed_config(toolkit)
|
73
|
-
|
74
|
-
# # Connection tests
|
75
|
-
# if options['check_connections']:
|
76
|
-
# self._test_connections(toolkit)
|
77
|
-
|
78
|
-
# except Exception as e:
|
79
|
-
# self.stdout.write(
|
80
|
-
# self.style.ERROR(f'❌ Configuration validation failed: {e}')
|
81
|
-
# )
|
82
|
-
# raise CommandError(f'Configuration error: {e}')
|
83
|
-
|
84
|
-
# self.stdout.write('=' * 60)
|
85
|
-
# self.stdout.write(
|
86
|
-
# self.style.SUCCESS('✨ Configuration validation completed!')
|
87
|
-
# )
|
88
|
-
|
89
|
-
# def _show_detailed_config(self, toolkit):
|
90
|
-
# """Show detailed configuration information."""
|
91
|
-
# self.stdout.write('\n📋 Detailed Configuration:')
|
92
|
-
|
93
|
-
# # Environment configuration
|
94
|
-
# self.stdout.write('\n🌍 Environment:')
|
95
|
-
# if hasattr(toolkit._config, 'env_mode'):
|
96
|
-
# self.stdout.write(f' Mode: {toolkit._config.env_mode}')
|
97
|
-
# self.stdout.write(f' Debug: {toolkit._config.debug}')
|
98
|
-
|
99
|
-
# # Database configuration
|
100
|
-
# self.stdout.write('\n🗄️ Database:')
|
101
|
-
# self.stdout.write(f' Engine: {toolkit.database_engine}')
|
102
|
-
# self.stdout.write(f' Name: {toolkit.database_name}')
|
103
|
-
# if hasattr(toolkit._db_config, 'has_multiple_databases'):
|
104
|
-
# self.stdout.write(f' Multiple DBs: {toolkit._db_config.has_multiple_databases}')
|
105
|
-
|
106
|
-
# # Security configuration
|
107
|
-
# self.stdout.write('\n🔒 Security:')
|
108
|
-
# self.stdout.write(f' CORS Enabled: {toolkit.cors_enabled}')
|
109
|
-
# self.stdout.write(f' CSRF Enabled: {toolkit.csrf_enabled}')
|
110
|
-
# self.stdout.write(f' SSL Redirect: {toolkit.ssl_enabled}')
|
111
|
-
|
112
|
-
# # API configuration
|
113
|
-
# self.stdout.write('\n🌐 API:')
|
114
|
-
# self.stdout.write(f' Page Size: {toolkit.api_page_size}')
|
115
|
-
# self.stdout.write(f' Rate Limiting: {toolkit.api_rate_limit_enabled}')
|
116
|
-
|
117
|
-
# # Cache configuration
|
118
|
-
# self.stdout.write('\n💾 Cache:')
|
119
|
-
# self.stdout.write(f' Backend: {toolkit.cache_backend}')
|
120
|
-
# self.stdout.write(f' Timeout: {toolkit.cache_timeout}s')
|
121
|
-
|
122
|
-
# # Extended features details
|
123
|
-
# if toolkit.unfold_enabled:
|
124
|
-
# self.stdout.write('\n🎨 Unfold:')
|
125
|
-
# self.stdout.write(f' Site Title: {toolkit.site_title}')
|
126
|
-
|
127
|
-
# if toolkit.revolution_enabled:
|
128
|
-
# self.stdout.write('\n🚀 Revolution:')
|
129
|
-
# self.stdout.write(f' API Prefix: {toolkit.api_prefix}')
|
130
|
-
|
131
|
-
# if toolkit.constance_enabled:
|
132
|
-
# self.stdout.write('\n⚙️ Constance:')
|
133
|
-
# self.stdout.write(f' Backend: {toolkit.constance_backend}')
|
134
|
-
|
135
|
-
# self.stdout.write('\n📝 Logging:')
|
136
|
-
# self.stdout.write(f' Log Level: {toolkit.log_level}')
|
137
|
-
|
138
|
-
# def _test_connections(self, toolkit):
|
139
|
-
# """Test database and cache connections."""
|
140
|
-
# self.stdout.write('\n🔗 Connection Tests:')
|
141
|
-
|
142
|
-
# # Test database connections
|
143
|
-
# self.stdout.write('\n🗄️ Database connections:')
|
144
|
-
# try:
|
145
|
-
# from django.db import connections
|
146
|
-
|
147
|
-
# for db_name in connections:
|
148
|
-
# try:
|
149
|
-
# connection = connections[db_name]
|
150
|
-
# with connection.cursor() as cursor:
|
151
|
-
# cursor.execute("SELECT 1")
|
152
|
-
# cursor.fetchone()
|
153
|
-
|
154
|
-
# self.stdout.write(
|
155
|
-
# self.style.SUCCESS(f' ✅ {db_name}: Connected')
|
156
|
-
# )
|
157
|
-
# except Exception as e:
|
158
|
-
# self.stdout.write(
|
159
|
-
# self.style.ERROR(f' ❌ {db_name}: {str(e)}')
|
160
|
-
# )
|
161
|
-
|
162
|
-
# except Exception as e:
|
163
|
-
# self.stdout.write(
|
164
|
-
# self.style.ERROR(f' ❌ Database test failed: {e}')
|
165
|
-
# )
|
166
|
-
|
167
|
-
# # Test cache connection
|
168
|
-
# self.stdout.write('\n💾 Cache connection:')
|
169
|
-
# try:
|
170
|
-
|
171
|
-
# test_key = 'config_validation_test'
|
172
|
-
# test_value = 'test_value'
|
173
|
-
|
174
|
-
# cache.set(test_key, test_value, 30)
|
175
|
-
# retrieved_value = cache.get(test_key)
|
176
|
-
|
177
|
-
# if retrieved_value == test_value:
|
178
|
-
# self.stdout.write(
|
179
|
-
# self.style.SUCCESS(' ✅ Cache: Working')
|
180
|
-
# )
|
181
|
-
# else:
|
182
|
-
# self.stdout.write(
|
183
|
-
# self.style.WARNING(' ⚠️ Cache: Read/write test failed')
|
184
|
-
# )
|
185
|
-
|
186
|
-
# except Exception as e:
|
187
|
-
# self.stdout.write(
|
188
|
-
# self.style.ERROR(f' ❌ Cache test failed: {e}')
|
189
|
-
# )
|
django_cfg/models/database.py
DELETED
@@ -1,480 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Database configuration models for django_cfg.
|
3
|
-
|
4
|
-
Following CRITICAL_REQUIREMENTS.md:
|
5
|
-
- No raw Dict/Any usage - everything through Pydantic models
|
6
|
-
- Proper type annotations for all fields
|
7
|
-
- No mutable default arguments
|
8
|
-
- Comprehensive validation with helpful error messages
|
9
|
-
"""
|
10
|
-
|
11
|
-
from typing import Dict, List, Optional, Any, Literal, Union
|
12
|
-
from pathlib import Path
|
13
|
-
from pydantic import BaseModel, Field, field_validator, model_validator, PrivateAttr
|
14
|
-
from urllib.parse import urlparse
|
15
|
-
|
16
|
-
from django_cfg.core.exceptions import DatabaseError, ValidationError
|
17
|
-
|
18
|
-
|
19
|
-
class DatabaseConfig(BaseModel):
|
20
|
-
"""
|
21
|
-
Type-safe database connection configuration.
|
22
|
-
|
23
|
-
Supports both individual connection parameters and connection strings.
|
24
|
-
Automatically validates connection parameters and provides helpful error messages.
|
25
|
-
"""
|
26
|
-
|
27
|
-
model_config = {
|
28
|
-
"str_strip_whitespace": True,
|
29
|
-
"validate_assignment": True,
|
30
|
-
"extra": "forbid", # Prevent typos in field names
|
31
|
-
}
|
32
|
-
|
33
|
-
# Core connection parameters
|
34
|
-
engine: Optional[str] = Field(
|
35
|
-
default=None,
|
36
|
-
description="Django database engine (e.g., 'django.db.backends.postgresql'). If not provided, will be auto-detected from database URL scheme.",
|
37
|
-
min_length=1,
|
38
|
-
)
|
39
|
-
|
40
|
-
name: str = Field(
|
41
|
-
...,
|
42
|
-
description="Database name or connection string",
|
43
|
-
min_length=1,
|
44
|
-
)
|
45
|
-
|
46
|
-
user: Optional[str] = Field(
|
47
|
-
default=None,
|
48
|
-
description="Database username",
|
49
|
-
)
|
50
|
-
|
51
|
-
password: Optional[str] = Field(
|
52
|
-
default=None,
|
53
|
-
description="Database password",
|
54
|
-
repr=False, # Don't show in repr for security
|
55
|
-
)
|
56
|
-
|
57
|
-
host: str = Field(
|
58
|
-
default="localhost",
|
59
|
-
description="Database host",
|
60
|
-
min_length=1,
|
61
|
-
)
|
62
|
-
|
63
|
-
port: int = Field(
|
64
|
-
default=5432,
|
65
|
-
description="Database port",
|
66
|
-
ge=1,
|
67
|
-
le=65535,
|
68
|
-
)
|
69
|
-
|
70
|
-
# Connection options
|
71
|
-
connect_timeout: int = Field(
|
72
|
-
default=10,
|
73
|
-
description="Connection timeout in seconds",
|
74
|
-
ge=1,
|
75
|
-
le=300, # Max 5 minutes
|
76
|
-
)
|
77
|
-
|
78
|
-
sslmode: str = Field(
|
79
|
-
default="prefer",
|
80
|
-
description="SSL mode for connection",
|
81
|
-
)
|
82
|
-
|
83
|
-
# Additional database options
|
84
|
-
options: Dict[str, Any] = Field(
|
85
|
-
default_factory=dict,
|
86
|
-
description="Additional database-specific options",
|
87
|
-
)
|
88
|
-
|
89
|
-
# Database routing configuration
|
90
|
-
apps: List[str] = Field(
|
91
|
-
default_factory=list,
|
92
|
-
description="Django app labels that should use this database",
|
93
|
-
)
|
94
|
-
|
95
|
-
operations: List[Literal["read", "write", "migrate"]] = Field(
|
96
|
-
default_factory=lambda: ["read", "write", "migrate"],
|
97
|
-
description="Allowed operations for this database",
|
98
|
-
min_length=1,
|
99
|
-
)
|
100
|
-
|
101
|
-
migrate_to: Optional[str] = Field(
|
102
|
-
default=None,
|
103
|
-
description="Override database alias for migrations (if different from this database)",
|
104
|
-
)
|
105
|
-
|
106
|
-
routing_description: str = Field(
|
107
|
-
default="",
|
108
|
-
description="Human-readable description of the routing rule",
|
109
|
-
)
|
110
|
-
|
111
|
-
# Internal fields for parsed connection strings
|
112
|
-
_is_connection_string: bool = PrivateAttr(default=False)
|
113
|
-
_parsed_components: Optional[Dict[str, Any]] = PrivateAttr(default=None)
|
114
|
-
|
115
|
-
@field_validator("engine")
|
116
|
-
@classmethod
|
117
|
-
def validate_engine(cls, v: Optional[str]) -> Optional[str]:
|
118
|
-
"""Validate Django database engine format."""
|
119
|
-
if v is None:
|
120
|
-
return v
|
121
|
-
|
122
|
-
if not v.startswith("django.db.backends."):
|
123
|
-
raise ValueError(f"Invalid database engine '{v}'. " "Must start with 'django.db.backends.'")
|
124
|
-
|
125
|
-
# Common engines validation
|
126
|
-
valid_engines = {
|
127
|
-
"django.db.backends.postgresql",
|
128
|
-
"django.db.backends.mysql",
|
129
|
-
"django.db.backends.sqlite3",
|
130
|
-
"django.db.backends.oracle",
|
131
|
-
}
|
132
|
-
|
133
|
-
if v not in valid_engines and not v.startswith("django.db.backends."):
|
134
|
-
# Allow custom backends but warn about common typos
|
135
|
-
common_typos = {
|
136
|
-
"postgresql": "django.db.backends.postgresql",
|
137
|
-
"postgres": "django.db.backends.postgresql",
|
138
|
-
"mysql": "django.db.backends.mysql",
|
139
|
-
"sqlite": "django.db.backends.sqlite3",
|
140
|
-
"sqlite3": "django.db.backends.sqlite3",
|
141
|
-
}
|
142
|
-
|
143
|
-
if v in common_typos:
|
144
|
-
raise ValueError(f"Invalid engine '{v}'. Did you mean '{common_typos[v]}'?")
|
145
|
-
|
146
|
-
return v
|
147
|
-
|
148
|
-
@field_validator("name")
|
149
|
-
@classmethod
|
150
|
-
def validate_name(cls, v: str) -> str:
|
151
|
-
"""Validate database name or parse connection string."""
|
152
|
-
# Check if it's a connection string
|
153
|
-
if "://" in v:
|
154
|
-
try:
|
155
|
-
parsed = urlparse(v)
|
156
|
-
if not parsed.scheme:
|
157
|
-
raise ValueError("Invalid connection string format")
|
158
|
-
return v
|
159
|
-
except Exception as e:
|
160
|
-
raise ValueError(f"Invalid connection string: {e}") from e
|
161
|
-
|
162
|
-
# Regular database name validation
|
163
|
-
if v in [":memory:", ""]:
|
164
|
-
return v # Special cases for SQLite
|
165
|
-
|
166
|
-
# Check for path-like names (SQLite files)
|
167
|
-
if "/" in v or "\\" in v or v.endswith(".db") or v.endswith(".sqlite3"):
|
168
|
-
path = Path(v)
|
169
|
-
if path.is_absolute() or v.startswith("./") or v.startswith("../"):
|
170
|
-
return v # Valid file path
|
171
|
-
|
172
|
-
return v
|
173
|
-
|
174
|
-
@field_validator("sslmode")
|
175
|
-
@classmethod
|
176
|
-
def validate_sslmode(cls, v: str) -> str:
|
177
|
-
"""Validate SSL mode values."""
|
178
|
-
valid_modes = {"disable", "allow", "prefer", "require", "verify-ca", "verify-full"}
|
179
|
-
|
180
|
-
if v not in valid_modes:
|
181
|
-
raise ValueError(f"Invalid SSL mode '{v}'. " f"Valid options: {', '.join(sorted(valid_modes))}")
|
182
|
-
|
183
|
-
return v
|
184
|
-
|
185
|
-
@model_validator(mode="before")
|
186
|
-
@classmethod
|
187
|
-
def validate_connection_consistency(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
188
|
-
"""Validate connection parameter consistency and auto-detect engine."""
|
189
|
-
# Auto-detect engine if not provided
|
190
|
-
if values.get("engine") is None and values.get("name"):
|
191
|
-
values["engine"] = cls._detect_engine_from_url(values["name"])
|
192
|
-
|
193
|
-
return values
|
194
|
-
|
195
|
-
@model_validator(mode="after")
|
196
|
-
def validate_connection_after(self) -> "DatabaseConfig":
|
197
|
-
"""Validate connection after model creation."""
|
198
|
-
# Parse connection string if present
|
199
|
-
if "://" in self.name:
|
200
|
-
self._is_connection_string = True
|
201
|
-
self._parsed_components = self._parse_connection_string(self.name)
|
202
|
-
|
203
|
-
# Override individual parameters with parsed values if not explicitly set
|
204
|
-
parsed = self._parsed_components
|
205
|
-
if parsed and not self.user and parsed.get("user"):
|
206
|
-
object.__setattr__(self, 'user', parsed["user"])
|
207
|
-
if parsed and not self.password and parsed.get("password"):
|
208
|
-
object.__setattr__(self, 'password', parsed["password"])
|
209
|
-
if parsed and self.host == "localhost" and parsed.get("host"):
|
210
|
-
object.__setattr__(self, 'host', parsed["host"])
|
211
|
-
if parsed and self.port == 5432 and parsed.get("port"):
|
212
|
-
object.__setattr__(self, 'port', parsed["port"])
|
213
|
-
|
214
|
-
# Validate SQLite-specific constraints
|
215
|
-
if self.engine == "django.db.backends.sqlite3":
|
216
|
-
if self.name not in [":memory:", ""] and not (self.name.endswith((".db", ".sqlite", ".sqlite3")) or "/" in self.name or "\\" in self.name):
|
217
|
-
raise ValueError("SQLite database name must be ':memory:', a file path, " "or end with .db, .sqlite, or .sqlite3")
|
218
|
-
|
219
|
-
# Validate PostgreSQL-specific constraints
|
220
|
-
elif self.engine == "django.db.backends.postgresql":
|
221
|
-
if not self._is_connection_string and not self.name:
|
222
|
-
raise ValueError("PostgreSQL database name is required")
|
223
|
-
|
224
|
-
return self
|
225
|
-
|
226
|
-
@staticmethod
|
227
|
-
def _detect_engine_from_url(url: str) -> str:
|
228
|
-
"""
|
229
|
-
Automatically detect Django database engine from URL scheme.
|
230
|
-
|
231
|
-
Args:
|
232
|
-
url: Database URL (e.g., 'postgresql://...', 'sqlite:///...')
|
233
|
-
|
234
|
-
Returns:
|
235
|
-
Django database engine string
|
236
|
-
|
237
|
-
Raises:
|
238
|
-
ValueError: If URL scheme is not supported
|
239
|
-
"""
|
240
|
-
if "://" not in url:
|
241
|
-
# Assume SQLite for file paths without scheme
|
242
|
-
return "django.db.backends.sqlite3"
|
243
|
-
|
244
|
-
scheme = url.split("://")[0].lower()
|
245
|
-
|
246
|
-
# Map URL schemes to Django engines
|
247
|
-
scheme_to_engine = {
|
248
|
-
"postgresql": "django.db.backends.postgresql",
|
249
|
-
"postgres": "django.db.backends.postgresql",
|
250
|
-
"mysql": "django.db.backends.mysql",
|
251
|
-
"sqlite": "django.db.backends.sqlite3",
|
252
|
-
"sqlite3": "django.db.backends.sqlite3",
|
253
|
-
"oracle": "django.db.backends.oracle",
|
254
|
-
}
|
255
|
-
|
256
|
-
if scheme in scheme_to_engine:
|
257
|
-
return scheme_to_engine[scheme]
|
258
|
-
|
259
|
-
raise ValueError(
|
260
|
-
f"Unsupported database scheme '{scheme}'. "
|
261
|
-
f"Supported schemes: {', '.join(scheme_to_engine.keys())}"
|
262
|
-
)
|
263
|
-
|
264
|
-
@staticmethod
|
265
|
-
def _parse_connection_string(connection_string: str) -> Dict[str, Any]:
|
266
|
-
"""Parse database connection string into components."""
|
267
|
-
try:
|
268
|
-
parsed = urlparse(connection_string)
|
269
|
-
|
270
|
-
components = {
|
271
|
-
"scheme": parsed.scheme,
|
272
|
-
"user": parsed.username,
|
273
|
-
"password": parsed.password,
|
274
|
-
"host": parsed.hostname,
|
275
|
-
"port": parsed.port,
|
276
|
-
"database": parsed.path.lstrip("/") if parsed.path else None,
|
277
|
-
}
|
278
|
-
|
279
|
-
# Parse query parameters as options
|
280
|
-
if parsed.query:
|
281
|
-
from urllib.parse import parse_qs
|
282
|
-
|
283
|
-
query_params = parse_qs(parsed.query)
|
284
|
-
components["options"] = {k: v[0] if len(v) == 1 else v for k, v in query_params.items()}
|
285
|
-
|
286
|
-
return components
|
287
|
-
|
288
|
-
except Exception as e:
|
289
|
-
raise DatabaseError(f"Failed to parse connection string: {e}", context={"connection_string": connection_string}) from e
|
290
|
-
|
291
|
-
def to_django_config(self) -> Dict[str, Any]:
|
292
|
-
"""
|
293
|
-
Convert to Django database configuration format.
|
294
|
-
|
295
|
-
Returns:
|
296
|
-
Django-compatible database configuration dictionary
|
297
|
-
|
298
|
-
Raises:
|
299
|
-
DatabaseError: If configuration cannot be converted
|
300
|
-
"""
|
301
|
-
try:
|
302
|
-
# Base configuration
|
303
|
-
config = {
|
304
|
-
"ENGINE": self.engine,
|
305
|
-
"OPTIONS": {**self.options},
|
306
|
-
}
|
307
|
-
|
308
|
-
# Add database-specific options
|
309
|
-
if self.engine == "django.db.backends.postgresql":
|
310
|
-
# PostgreSQL supports connect_timeout and sslmode
|
311
|
-
config["OPTIONS"]["connect_timeout"] = self.connect_timeout
|
312
|
-
config["OPTIONS"]["sslmode"] = self.sslmode
|
313
|
-
elif self.engine == "django.db.backends.mysql":
|
314
|
-
# MySQL supports connect_timeout but not sslmode
|
315
|
-
config["OPTIONS"]["connect_timeout"] = self.connect_timeout
|
316
|
-
# SQLite doesn't support connect_timeout or sslmode, so we skip them
|
317
|
-
|
318
|
-
# Handle connection string vs individual parameters
|
319
|
-
if self._is_connection_string:
|
320
|
-
# For connection strings, use the full string as NAME
|
321
|
-
config["NAME"] = self.name
|
322
|
-
|
323
|
-
# Add parsed components if available
|
324
|
-
if self._parsed_components:
|
325
|
-
parsed = self._parsed_components
|
326
|
-
if parsed.get("database"):
|
327
|
-
config["NAME"] = parsed["database"]
|
328
|
-
if parsed.get("user"):
|
329
|
-
config["USER"] = parsed["user"]
|
330
|
-
if parsed.get("password"):
|
331
|
-
config["PASSWORD"] = parsed["password"]
|
332
|
-
if parsed.get("host"):
|
333
|
-
config["HOST"] = parsed["host"]
|
334
|
-
if parsed.get("port"):
|
335
|
-
config["PORT"] = parsed["port"]
|
336
|
-
if parsed.get("options"):
|
337
|
-
config["OPTIONS"].update(parsed["options"])
|
338
|
-
else:
|
339
|
-
# Individual parameters
|
340
|
-
config["NAME"] = self.name
|
341
|
-
|
342
|
-
if self.user:
|
343
|
-
config["USER"] = self.user
|
344
|
-
|
345
|
-
if self.password:
|
346
|
-
config["PASSWORD"] = self.password
|
347
|
-
|
348
|
-
if self.host:
|
349
|
-
config["HOST"] = self.host
|
350
|
-
|
351
|
-
if self.port:
|
352
|
-
config["PORT"] = self.port
|
353
|
-
|
354
|
-
return config
|
355
|
-
|
356
|
-
except Exception as e:
|
357
|
-
raise DatabaseError(f"Failed to convert database configuration: {e}", database_alias=getattr(self, "_alias", "unknown"), context={"config": self.model_dump()}) from e
|
358
|
-
|
359
|
-
@field_validator("apps")
|
360
|
-
@classmethod
|
361
|
-
def validate_apps(cls, v: List[str]) -> List[str]:
|
362
|
-
"""Validate app labels format."""
|
363
|
-
for app in v:
|
364
|
-
if not app or not app.replace("_", "").replace(".", "").isalnum():
|
365
|
-
raise ValueError(f"Invalid app label '{app}'. " "App labels must contain only letters, numbers, dots, and underscores")
|
366
|
-
return v
|
367
|
-
|
368
|
-
@field_validator("operations")
|
369
|
-
@classmethod
|
370
|
-
def validate_operations(cls, v: List[str]) -> List[str]:
|
371
|
-
"""Validate operations list."""
|
372
|
-
if not v:
|
373
|
-
raise ValueError("At least one operation must be specified")
|
374
|
-
|
375
|
-
valid_ops = {"read", "write", "migrate"}
|
376
|
-
for op in v:
|
377
|
-
if op not in valid_ops:
|
378
|
-
raise ValueError(f"Invalid operation '{op}'. " f"Valid operations: {', '.join(sorted(valid_ops))}")
|
379
|
-
return v
|
380
|
-
|
381
|
-
def matches_app(self, app_label: str) -> bool:
|
382
|
-
"""
|
383
|
-
Check if this database should be used for the given app.
|
384
|
-
|
385
|
-
Args:
|
386
|
-
app_label: Django app label to check
|
387
|
-
|
388
|
-
Returns:
|
389
|
-
True if this database should be used for the app
|
390
|
-
"""
|
391
|
-
return app_label in self.apps
|
392
|
-
|
393
|
-
def allows_operation(self, operation: str) -> bool:
|
394
|
-
"""
|
395
|
-
Check if this database allows the given operation.
|
396
|
-
|
397
|
-
Args:
|
398
|
-
operation: Operation to check ('read', 'write', 'migrate')
|
399
|
-
|
400
|
-
Returns:
|
401
|
-
True if operation is allowed
|
402
|
-
"""
|
403
|
-
return operation in self.operations
|
404
|
-
|
405
|
-
def get_migration_database(self) -> Optional[str]:
|
406
|
-
"""
|
407
|
-
Get the database alias to use for migrations.
|
408
|
-
|
409
|
-
Returns:
|
410
|
-
Database alias for migrations, or None to use this database
|
411
|
-
"""
|
412
|
-
return self.migrate_to
|
413
|
-
|
414
|
-
def has_routing_rules(self) -> bool:
|
415
|
-
"""
|
416
|
-
Check if this database has any routing rules configured.
|
417
|
-
|
418
|
-
Returns:
|
419
|
-
True if apps list is not empty
|
420
|
-
"""
|
421
|
-
return bool(self.apps)
|
422
|
-
|
423
|
-
def test_connection(self) -> bool:
|
424
|
-
"""
|
425
|
-
Test database connection (placeholder for future implementation).
|
426
|
-
|
427
|
-
Returns:
|
428
|
-
True if connection successful, False otherwise
|
429
|
-
"""
|
430
|
-
# TODO: Implement actual connection testing
|
431
|
-
# This would require Django to be available and configured
|
432
|
-
return True
|
433
|
-
|
434
|
-
@classmethod
|
435
|
-
def from_url(
|
436
|
-
cls,
|
437
|
-
url: str,
|
438
|
-
*,
|
439
|
-
apps: Optional[List[str]] = None,
|
440
|
-
operations: Optional[List[Literal["read", "write", "migrate"]]] = None,
|
441
|
-
routing_description: str = "",
|
442
|
-
**kwargs
|
443
|
-
) -> "DatabaseConfig":
|
444
|
-
"""
|
445
|
-
Create DatabaseConfig from URL with automatic engine detection.
|
446
|
-
|
447
|
-
Args:
|
448
|
-
url: Database URL (e.g., 'postgresql://user:pass@host:port/db')
|
449
|
-
apps: Django app labels that should use this database
|
450
|
-
operations: Allowed operations for this database
|
451
|
-
routing_description: Human-readable description of the routing rule
|
452
|
-
**kwargs: Additional parameters to override defaults
|
453
|
-
|
454
|
-
Returns:
|
455
|
-
DatabaseConfig instance with auto-detected engine
|
456
|
-
|
457
|
-
Example:
|
458
|
-
# Simple SQLite database
|
459
|
-
db = DatabaseConfig.from_url("sqlite:///db.sqlite3")
|
460
|
-
|
461
|
-
# PostgreSQL with routing
|
462
|
-
blog_db = DatabaseConfig.from_url(
|
463
|
-
"postgresql://user:pass@localhost:5432/blog",
|
464
|
-
apps=["apps.blog"],
|
465
|
-
routing_description="Blog posts and comments"
|
466
|
-
)
|
467
|
-
"""
|
468
|
-
return cls(
|
469
|
-
name=url,
|
470
|
-
apps=apps or [],
|
471
|
-
operations=operations or ["read", "write", "migrate"],
|
472
|
-
routing_description=routing_description,
|
473
|
-
**kwargs
|
474
|
-
)
|
475
|
-
|
476
|
-
|
477
|
-
# Export all models
|
478
|
-
__all__ = [
|
479
|
-
"DatabaseConfig",
|
480
|
-
]
|