ntc-dev-kit 1.1.0__tar.gz → 1.2.1__tar.gz
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.
- {ntc_dev_kit-1.1.0/ntc_dev_kit.egg-info → ntc_dev_kit-1.2.1}/PKG-INFO +26 -2
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/README.md +25 -1
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/__init__.py +1 -1
- ntc_dev_kit-1.2.1/ntc/cli_enhanced.py +421 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/main.py +2 -2
- ntc_dev_kit-1.2.1/ntc/templates/auth_app/migrations/0001_initial.py +51 -0
- ntc_dev_kit-1.2.1/ntc/templates/auth_app/models.py +54 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/serializers.py +8 -1
- ntc_dev_kit-1.2.1/ntc/templates/auth_app/tasks.py +28 -0
- ntc_dev_kit-1.2.1/ntc/templates/auth_app/templates/authentication/password_reset.html +5 -0
- ntc_dev_kit-1.2.1/ntc/templates/auth_app/templates/authentication/password_reset.txt +9 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/tests.py +51 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/views.py +25 -11
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/main.py +24 -20
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/structure.json +26 -1
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/.entry/start_tasks_backend.sh +2 -1
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/CLAUDE.md +19 -1
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/common_apps.py +3 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/apps/common_checks.py +17 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/ci/github_ci.yml +28 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/ci/pre_commit.yaml +7 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/exceptions/handler.py +5 -8
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/health.py +68 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/renderers.py +15 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/response_wrapper.py +27 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/schema.py +39 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/core/tests.py +60 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/django_settings.txt +50 -6
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/prod_settings.py +5 -0
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/django_project/schema.py +19 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/test_settings.py +2 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/urls.py +4 -1
- ntc_dev_kit-1.2.1/ntc/templates/drf_structure/templates/docker/docker-compose.yml +65 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/knowledge/knowledge_description.md +1 -1
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/knowledge/knowledge_design_pattern.md +12 -19
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/README.md +13 -11
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/django_requirements.txt +1 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/services/api.service.ts +5 -5
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/services/base.service.ts +1 -1
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/output.py +10 -0
- ntc_dev_kit-1.2.1/ntc/utils/prompts.py +280 -0
- ntc_dev_kit-1.2.1/ntc/wizard.py +320 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1/ntc_dev_kit.egg-info}/PKG-INFO +26 -2
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/SOURCES.txt +13 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/pyproject.toml +1 -1
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/setup.py +1 -1
- ntc_dev_kit-1.1.0/ntc/cli_enhanced.py +0 -934
- ntc_dev_kit-1.1.0/ntc/templates/auth_app/models.py +0 -30
- ntc_dev_kit-1.1.0/ntc/templates/drf_structure/templates/core/response_wrapper.py +0 -12
- ntc_dev_kit-1.1.0/ntc/templates/drf_structure/templates/django_project/schema.py +0 -8
- ntc_dev_kit-1.1.0/ntc/templates/drf_structure/templates/docker/docker-compose.yml +0 -11
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/LICENSE +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/MANIFEST.in +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/app_creator.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/cli.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/composer/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/composer/main.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/deployment/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/admin.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/apps.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/auth_app/urls.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/drf_structure.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/.entry/start_core_backend.sh +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/README.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_admin.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_apps.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_models.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_urls.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/app_views.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/apps/common_models.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/config/gunicorn_config.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/config/http.conf +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/formatter.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/logger.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/pagination.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/azure/email/backend.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/azure/email/client.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/azure/email/notifier.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/services/object_storage.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/core/utils/request.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/asgi.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/celery.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/dev_settings.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/project_init.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/django_project/wsgi.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/docker/Dockerfile +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/docker/captain-definition +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/django_env.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/django_requirements_dev.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/dockerignore.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/gitattributes.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/gitignore.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/manage.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/pytest_ini.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/drf_structure/templates/root/ruff_toml.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.gitignore +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/.knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/CLAUDE.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/analysis_options.yaml +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/config/app_config.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/config/environment.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/constants/app_constants.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/core/network/dio_client.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/features/home/presentation/home_screen.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/l10n/app_localizations.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/main.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/services/api_service.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/shared/localization/locale_provider.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/shared/theme/app_theme.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/lib/shared/theme/theme_provider.dart +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/main.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/pubspec.yaml +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/flutter/structure.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/.env.example +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/.gitignore +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/CLAUDE.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/README.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/eslint.config.mjs +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/main.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/next.config.js +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/package.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/app/globals.css +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/app/layout.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/app/page.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/components/Header.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/services/api.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/services/server.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/src/utils/constants.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/structure.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/nextjs/tsconfig.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/.env.example +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/.gitignore +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/CLAUDE.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/README.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/eslint.config.js +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/index.html +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/main.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/package.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/App.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/components/ProtectedRoute.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/components/Sidebar.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/components/TopNav.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/context/AuthContext.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/context/ThemeContext.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/context/WorkspaceContext.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/hooks/useApi.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/index.css +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/layouts/AuthLayout.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/layouts/DashboardLayout.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/main.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/pages/DashboardPage.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/pages/auth/LoginPage.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/services/token.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/src/vite-env.d.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/structure.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/tsconfig.app.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/tsconfig.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/tsconfig.node.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_admin/vite.config.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/App.css +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/CLAUDE.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/index.css +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/index.html +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/knowledge/knowledge_description.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/knowledge/knowledge_design_pattern.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/knowledge/knowledge_structure.md +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/main.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/package.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/App.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/components/Header.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/main.tsx +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/services/api.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/utils/constants.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/src/vite-env.d.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/structure.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/tsconfig.app.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/tsconfig.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/tsconfig.node.json +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/templates/react_structure/vite.config.ts +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/ui/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/ui/app.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/__init__.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/environment.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/shell.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc/utils/validation.py +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/dependency_links.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/entry_points.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/requires.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/ntc_dev_kit.egg-info/top_level.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/requirements.txt +0 -0
- {ntc_dev_kit-1.1.0 → ntc_dev_kit-1.2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ntc-dev-kit
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Nexent Toolkit Console - A CLI tool for scaffolding Django REST Framework, React, Next.js, and Flutter projects
|
|
5
5
|
Home-page: https://github.com/nexent/ntc
|
|
6
6
|
Author: Nexent
|
|
@@ -97,7 +97,31 @@ ntc init --technology flutter
|
|
|
97
97
|
ntc init
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
A few questions, asked inline: project name, what you're building, where it goes, then the
|
|
101
|
+
options for that technology (sign-in and Python environment for Django; installing dependencies
|
|
102
|
+
for the others), a summary, and **Create?**. The defaults are already selected, so Enter takes
|
|
103
|
+
them; **↑/↓** move, a **number key** picks an option at once, **Esc** cancels without writing
|
|
104
|
+
anything. Each answer collapses to one line, and the only progress shown is for real work
|
|
105
|
+
(creating the environment, installing packages).
|
|
106
|
+
|
|
107
|
+
### Without questions
|
|
108
|
+
|
|
109
|
+
Every answer can be given as a flag; with `--yes` nothing is asked (scripts, CI, AI agents):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
ntc init -t django --name shop-api --with-auth --env venv --yes
|
|
113
|
+
ntc init -t nextjs --name site --install --yes
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
| Flag | Meaning |
|
|
117
|
+
|------|---------|
|
|
118
|
+
| `-t`, `--technology` | `django`, `nextjs`, `react-admin`, `react`, `flutter` |
|
|
119
|
+
| `--name` | Project name (`.` = the current folder) |
|
|
120
|
+
| `--path` | Folder to create the project in (default: current folder) |
|
|
121
|
+
| `--with-auth` / `--no-auth` | Django: include the sign-in app (custom User model, JWT, migration included) |
|
|
122
|
+
| `--env` | Django: `venv`, `conda`, `existing` (the active virtualenv) or `skip` |
|
|
123
|
+
| `--install` / `--no-install` | Others: run `npm install` / `flutter pub get` |
|
|
124
|
+
| `--yes`, `-y` | Ask nothing; needs `--name` and `--technology` |
|
|
101
125
|
|
|
102
126
|
### Technology Options
|
|
103
127
|
|
|
@@ -57,7 +57,31 @@ ntc init --technology flutter
|
|
|
57
57
|
ntc init
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
A few questions, asked inline: project name, what you're building, where it goes, then the
|
|
61
|
+
options for that technology (sign-in and Python environment for Django; installing dependencies
|
|
62
|
+
for the others), a summary, and **Create?**. The defaults are already selected, so Enter takes
|
|
63
|
+
them; **↑/↓** move, a **number key** picks an option at once, **Esc** cancels without writing
|
|
64
|
+
anything. Each answer collapses to one line, and the only progress shown is for real work
|
|
65
|
+
(creating the environment, installing packages).
|
|
66
|
+
|
|
67
|
+
### Without questions
|
|
68
|
+
|
|
69
|
+
Every answer can be given as a flag; with `--yes` nothing is asked (scripts, CI, AI agents):
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ntc init -t django --name shop-api --with-auth --env venv --yes
|
|
73
|
+
ntc init -t nextjs --name site --install --yes
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Flag | Meaning |
|
|
77
|
+
|------|---------|
|
|
78
|
+
| `-t`, `--technology` | `django`, `nextjs`, `react-admin`, `react`, `flutter` |
|
|
79
|
+
| `--name` | Project name (`.` = the current folder) |
|
|
80
|
+
| `--path` | Folder to create the project in (default: current folder) |
|
|
81
|
+
| `--with-auth` / `--no-auth` | Django: include the sign-in app (custom User model, JWT, migration included) |
|
|
82
|
+
| `--env` | Django: `venv`, `conda`, `existing` (the active virtualenv) or `skip` |
|
|
83
|
+
| `--install` / `--no-install` | Others: run `npm install` / `flutter pub get` |
|
|
84
|
+
| `--yes`, `-y` | Ask nothing; needs `--name` and `--technology` |
|
|
61
85
|
|
|
62
86
|
### Technology Options
|
|
63
87
|
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
from colorama import Fore, Style, init
|
|
6
|
+
from ntc.utils.validation import is_valid_project_name
|
|
7
|
+
|
|
8
|
+
# Commands import their generators when they run, so `ntc --help` and the first question of
|
|
9
|
+
# `ntc init` appear without loading every template module first.
|
|
10
|
+
|
|
11
|
+
# Initialize colorama for cross-platform colored output
|
|
12
|
+
init(autoreset=True)
|
|
13
|
+
|
|
14
|
+
def print_banner():
|
|
15
|
+
"""Display minimal welcome banner."""
|
|
16
|
+
print(f"\n{Fore.WHITE}{Style.BRIGHT} ntc{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}— project scaffolding tool{Style.RESET_ALL}")
|
|
17
|
+
print()
|
|
18
|
+
|
|
19
|
+
def print_success(message):
|
|
20
|
+
"""Print success message with green color and checkmark."""
|
|
21
|
+
print(f"{Fore.GREEN}✅ {message}{Style.RESET_ALL}")
|
|
22
|
+
|
|
23
|
+
def print_error(message):
|
|
24
|
+
"""Print error message with red color and X mark."""
|
|
25
|
+
print(f"{Fore.RED}❌ {message}{Style.RESET_ALL}")
|
|
26
|
+
|
|
27
|
+
def print_warning(message):
|
|
28
|
+
"""Print warning message with yellow color and warning sign."""
|
|
29
|
+
print(f"{Fore.YELLOW}⚠️ {message}{Style.RESET_ALL}")
|
|
30
|
+
|
|
31
|
+
def print_info(message):
|
|
32
|
+
"""Print info message with blue color and info icon."""
|
|
33
|
+
print(f"{Fore.CYAN}ℹ️ {message}{Style.RESET_ALL}")
|
|
34
|
+
|
|
35
|
+
def animate_dependency_install(requirements_path=None, pip_command=None, env_type="system"):
|
|
36
|
+
"""Show real dependency installation with progress animation."""
|
|
37
|
+
import subprocess
|
|
38
|
+
import threading
|
|
39
|
+
|
|
40
|
+
if not requirements_path or not os.path.exists(requirements_path):
|
|
41
|
+
# Fallback to simulation if no requirements file
|
|
42
|
+
dependencies = [
|
|
43
|
+
"Resolving dependencies",
|
|
44
|
+
"Downloading packages",
|
|
45
|
+
"Installing Django",
|
|
46
|
+
"Installing DRF",
|
|
47
|
+
"Installing Celery",
|
|
48
|
+
"Installing CORS headers",
|
|
49
|
+
"Configuring environment",
|
|
50
|
+
"Setting up database",
|
|
51
|
+
"Creating migrations",
|
|
52
|
+
"Finalizing setup"
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
print(f"\n{Fore.MAGENTA}📦 Installing Dependencies{Style.RESET_ALL}")
|
|
56
|
+
|
|
57
|
+
for i, dep in enumerate(dependencies, 1):
|
|
58
|
+
progress = int((i / len(dependencies)) * 100)
|
|
59
|
+
|
|
60
|
+
# Show animation for each dependency
|
|
61
|
+
for j in range(5): # 5 animation cycles per dependency
|
|
62
|
+
frame = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][j % 10]
|
|
63
|
+
# Clear line and print new content
|
|
64
|
+
print(f"\r{' ' * 80}\r{Fore.CYAN}{frame} {dep}... {Fore.YELLOW}[{progress}%]{Style.RESET_ALL}", end="", flush=True)
|
|
65
|
+
time.sleep(0.2)
|
|
66
|
+
|
|
67
|
+
print(f"\r{' ' * 80}\r{Fore.GREEN}✅ All dependencies installed successfully! {Fore.YELLOW}[100%]{Style.RESET_ALL}")
|
|
68
|
+
print()
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
# Real dependency installation with animation
|
|
72
|
+
print(f"\n{Fore.MAGENTA}📦 Installing Dependencies{Style.RESET_ALL}")
|
|
73
|
+
|
|
74
|
+
# Determine the pip command to use
|
|
75
|
+
if pip_command is None:
|
|
76
|
+
# Default to system pip
|
|
77
|
+
pip_command = [sys.executable, "-m", "pip", "install", "-r", requirements_path]
|
|
78
|
+
|
|
79
|
+
# Animation state
|
|
80
|
+
animation_running = True
|
|
81
|
+
install_complete = False
|
|
82
|
+
install_success = False
|
|
83
|
+
error_message = ""
|
|
84
|
+
|
|
85
|
+
def run_pip_install():
|
|
86
|
+
nonlocal install_complete, install_success, error_message
|
|
87
|
+
try:
|
|
88
|
+
# Run pip install with suppressed output
|
|
89
|
+
result = subprocess.run(
|
|
90
|
+
pip_command,
|
|
91
|
+
capture_output=True,
|
|
92
|
+
text=True,
|
|
93
|
+
check=True
|
|
94
|
+
)
|
|
95
|
+
install_success = True
|
|
96
|
+
except subprocess.CalledProcessError as e:
|
|
97
|
+
install_success = False
|
|
98
|
+
error_message = str(e)
|
|
99
|
+
finally:
|
|
100
|
+
install_complete = True
|
|
101
|
+
|
|
102
|
+
# Start installation in background thread
|
|
103
|
+
install_thread = threading.Thread(target=run_pip_install)
|
|
104
|
+
install_thread.start()
|
|
105
|
+
|
|
106
|
+
# Show animation while installation runs
|
|
107
|
+
frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
|
108
|
+
frame_index = 0
|
|
109
|
+
|
|
110
|
+
while not install_complete:
|
|
111
|
+
frame = frames[frame_index % len(frames)]
|
|
112
|
+
# Clear line completely before writing new content
|
|
113
|
+
env_msg = f" ({env_type} environment)" if env_type != "system" else ""
|
|
114
|
+
print(f"\r{' ' * 80}\r{Fore.CYAN}{frame} Installing packages{env_msg}...{Style.RESET_ALL}", end="", flush=True)
|
|
115
|
+
time.sleep(0.1)
|
|
116
|
+
frame_index += 1
|
|
117
|
+
|
|
118
|
+
# Wait for thread to complete
|
|
119
|
+
install_thread.join()
|
|
120
|
+
|
|
121
|
+
# Show final result
|
|
122
|
+
if install_success:
|
|
123
|
+
print(f"\r{' ' * 80}\r{Fore.GREEN}✅ All dependencies installed successfully!{Style.RESET_ALL}")
|
|
124
|
+
else:
|
|
125
|
+
print(f"\r{' ' * 80}\r{Fore.RED}❌ Failed to install dependencies: {error_message}{Style.RESET_ALL}")
|
|
126
|
+
print(f"{Fore.YELLOW}💡 You can manually install later with: pip install -r requirements.txt{Style.RESET_ALL}")
|
|
127
|
+
|
|
128
|
+
print()
|
|
129
|
+
|
|
130
|
+
def run_doctor():
|
|
131
|
+
"""Check setup and fix common issues like PATH configuration."""
|
|
132
|
+
import shutil
|
|
133
|
+
import site
|
|
134
|
+
|
|
135
|
+
# Nexent brand palette
|
|
136
|
+
DIM = Fore.LIGHTBLACK_EX
|
|
137
|
+
ACCENT = Fore.WHITE
|
|
138
|
+
OK = Fore.GREEN
|
|
139
|
+
WARN = Fore.YELLOW
|
|
140
|
+
ERR = Fore.RED
|
|
141
|
+
BOLD = Style.BRIGHT
|
|
142
|
+
RESET = Style.RESET_ALL
|
|
143
|
+
|
|
144
|
+
def _ok(text):
|
|
145
|
+
print(f" {OK} {text}{RESET}")
|
|
146
|
+
|
|
147
|
+
def _err(text):
|
|
148
|
+
print(f" {ERR} {text}{RESET}")
|
|
149
|
+
|
|
150
|
+
def _warn(text):
|
|
151
|
+
print(f" {WARN} {text}{RESET}")
|
|
152
|
+
|
|
153
|
+
def _dim(text):
|
|
154
|
+
print(f" {DIM}{text}{RESET}")
|
|
155
|
+
|
|
156
|
+
# Minimal Nexent header
|
|
157
|
+
print(f"\n{BOLD}{Fore.WHITE} NEXENT{RESET}")
|
|
158
|
+
print(f" {DIM}Future Awaits{RESET}")
|
|
159
|
+
print(f" {DIM}{'─' * 50}{RESET}")
|
|
160
|
+
print(f" {Fore.WHITE}SETUP DIAGNOSTICS{RESET}\n")
|
|
161
|
+
|
|
162
|
+
issues_found = 0
|
|
163
|
+
issues_fixed = 0
|
|
164
|
+
|
|
165
|
+
# 1. Python
|
|
166
|
+
print(f" {Fore.WHITE}Python{RESET}")
|
|
167
|
+
py_version = sys.version_info
|
|
168
|
+
if py_version >= (3, 10):
|
|
169
|
+
_ok(f"{py_version.major}.{py_version.minor}.{py_version.micro}")
|
|
170
|
+
else:
|
|
171
|
+
_err(f"{py_version.major}.{py_version.minor} — >= 3.10 required")
|
|
172
|
+
issues_found += 1
|
|
173
|
+
|
|
174
|
+
# 2. Dependencies
|
|
175
|
+
print(f"\n {Fore.WHITE}Dependencies{RESET}")
|
|
176
|
+
deps_ok = True
|
|
177
|
+
for mod, name in [("colorama", "colorama"), ("yaml", "pyyaml")]:
|
|
178
|
+
try:
|
|
179
|
+
__import__(mod)
|
|
180
|
+
_ok(name)
|
|
181
|
+
except ImportError:
|
|
182
|
+
_err(name)
|
|
183
|
+
deps_ok = False
|
|
184
|
+
if not deps_ok:
|
|
185
|
+
issues_found += 1
|
|
186
|
+
_dim(" Run: pip install colorama pyyaml")
|
|
187
|
+
|
|
188
|
+
# 3. Command accessibility
|
|
189
|
+
print(f"\n {Fore.WHITE}Command{RESET}")
|
|
190
|
+
ntc_path = shutil.which("ntc")
|
|
191
|
+
if ntc_path:
|
|
192
|
+
_ok("ntc found")
|
|
193
|
+
_dim(f" {ntc_path}")
|
|
194
|
+
else:
|
|
195
|
+
_err("ntc not in PATH")
|
|
196
|
+
issues_found += 1
|
|
197
|
+
|
|
198
|
+
# 4. Scripts directory
|
|
199
|
+
print(f"\n {Fore.WHITE}Scripts Directory{RESET}")
|
|
200
|
+
scripts_dir = os.path.join(site.USER_BASE, "Scripts")
|
|
201
|
+
if os.path.exists(scripts_dir):
|
|
202
|
+
_ok(scripts_dir)
|
|
203
|
+
else:
|
|
204
|
+
_warn("missing — creating...")
|
|
205
|
+
os.makedirs(scripts_dir, exist_ok=True)
|
|
206
|
+
_ok("created")
|
|
207
|
+
issues_fixed += 1
|
|
208
|
+
|
|
209
|
+
# 5. PATH
|
|
210
|
+
if os.name == "nt":
|
|
211
|
+
print(f"\n {Fore.WHITE}PATH{RESET}")
|
|
212
|
+
current_path = os.environ.get("PATH", "")
|
|
213
|
+
if scripts_dir.lower() in current_path.lower():
|
|
214
|
+
_ok("Scripts directory in PATH")
|
|
215
|
+
else:
|
|
216
|
+
_warn("Scripts directory not in PATH")
|
|
217
|
+
try:
|
|
218
|
+
import winreg
|
|
219
|
+
with winreg.OpenKey(
|
|
220
|
+
winreg.HKEY_CURRENT_USER, "Environment", 0,
|
|
221
|
+
winreg.KEY_READ | winreg.KEY_WRITE,
|
|
222
|
+
) as key:
|
|
223
|
+
existing, _ = winreg.QueryValueEx(key, "PATH")
|
|
224
|
+
if scripts_dir.lower() not in existing.lower():
|
|
225
|
+
new_path = f"{existing};{scripts_dir}"
|
|
226
|
+
winreg.SetValueEx(key, "PATH", 0, winreg.REG_EXPAND_SZ, new_path)
|
|
227
|
+
os.environ["PATH"] = new_path
|
|
228
|
+
_ok("added to registry")
|
|
229
|
+
issues_fixed += 1
|
|
230
|
+
else:
|
|
231
|
+
_ok("in registry (restart needed)")
|
|
232
|
+
except Exception as e:
|
|
233
|
+
_err(f"registry edit failed: {e}")
|
|
234
|
+
_dim(f" Add manually: {scripts_dir}")
|
|
235
|
+
issues_found += 1
|
|
236
|
+
|
|
237
|
+
# 6. Package version
|
|
238
|
+
print(f"\n {Fore.WHITE}Package{RESET}")
|
|
239
|
+
try:
|
|
240
|
+
import ntc
|
|
241
|
+
_ok(f"ntc-dev-kit {ntc.__version__}")
|
|
242
|
+
except (ImportError, AttributeError):
|
|
243
|
+
_err("ntc-dev-kit not installed")
|
|
244
|
+
issues_found += 1
|
|
245
|
+
|
|
246
|
+
# Summary
|
|
247
|
+
print(f"\n {DIM}{'─' * 50}{RESET}")
|
|
248
|
+
if issues_found == 0 and issues_fixed == 0:
|
|
249
|
+
print(f" {OK}Ready.{RESET}")
|
|
250
|
+
elif issues_fixed > 0:
|
|
251
|
+
print(f" {OK}Fixed {issues_fixed}.{RESET}")
|
|
252
|
+
if issues_found > 0:
|
|
253
|
+
print(f" {WARN}{issues_found} remaining.{RESET}")
|
|
254
|
+
else:
|
|
255
|
+
print(f" {DIM}Restart terminal for changes.{RESET}")
|
|
256
|
+
else:
|
|
257
|
+
print(f" {ERR}{issues_found} issue(s) found.{RESET}")
|
|
258
|
+
print(f" {DIM}Run 'ntc doctor' again after fixing.{RESET}")
|
|
259
|
+
print(f" {DIM}{'─' * 50}{RESET}\n")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _use_utf8_console():
|
|
263
|
+
"""Print emoji and box-drawing characters on any console.
|
|
264
|
+
|
|
265
|
+
A Windows console on cp1252 (the default, and what scripts, CI and editor terminals get)
|
|
266
|
+
can't encode them: the very first print raised UnicodeEncodeError and ntc init crashed.
|
|
267
|
+
Characters the console truly can't show are replaced instead of raising."""
|
|
268
|
+
for stream in (sys.stdout, sys.stderr):
|
|
269
|
+
try:
|
|
270
|
+
stream.reconfigure(encoding='utf-8', errors='replace')
|
|
271
|
+
except (AttributeError, ValueError):
|
|
272
|
+
pass # not a reconfigurable text stream (redirected to an object, Python < 3.7)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def main():
|
|
276
|
+
_use_utf8_console()
|
|
277
|
+
parser = argparse.ArgumentParser(
|
|
278
|
+
description="ntc — project scaffolding tool",
|
|
279
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
280
|
+
epilog="""
|
|
281
|
+
Examples:
|
|
282
|
+
ntc init # New project: a few questions, defaults pre-selected
|
|
283
|
+
ntc init -t django # Skip the "what are you building" question
|
|
284
|
+
ntc init -t django --name shop-api --with-auth --env venv --yes
|
|
285
|
+
# No questions at all (scripts, CI, AI agents)
|
|
286
|
+
ntc init -t nextjs --name site --install --yes
|
|
287
|
+
ntc compose myproject # Compose full-stack project with multiple components
|
|
288
|
+
ntc start # Start all services from ntc.yml configuration
|
|
289
|
+
ntc create app myapp # Create new Django app
|
|
290
|
+
ntc drf init path name # Legacy Django REST Framework initialization
|
|
291
|
+
"""
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
295
|
+
|
|
296
|
+
# Enhanced init command
|
|
297
|
+
init_parser = subparsers.add_parser("init", help="Interactive project initialization")
|
|
298
|
+
init_parser.add_argument('--technology', '-t', type=str,
|
|
299
|
+
choices=['django', 'react', 'react-admin', 'nextjs', 'flutter'],
|
|
300
|
+
help='Technology to use (django, react, react-admin, nextjs, flutter)')
|
|
301
|
+
init_parser.add_argument("--backend",
|
|
302
|
+
choices=["django", "drf", "laravel"],
|
|
303
|
+
help="Backend technology")
|
|
304
|
+
init_parser.add_argument("--frontend",
|
|
305
|
+
choices=["react", "react-admin", "nextjs", "flutter"],
|
|
306
|
+
help="Frontend technology")
|
|
307
|
+
init_parser.add_argument("--name", help="Project name ('.' = the current directory's name)")
|
|
308
|
+
init_parser.add_argument("--path", help="Directory to create the project in (default: current directory)")
|
|
309
|
+
auth_group = init_parser.add_mutually_exclusive_group()
|
|
310
|
+
auth_group.add_argument("--with-auth", dest="with_auth", action="store_true", default=None,
|
|
311
|
+
help="Django: include the authentication app (custom User model, JWT endpoints)")
|
|
312
|
+
auth_group.add_argument("--no-auth", dest="with_auth", action="store_false",
|
|
313
|
+
help="Django: no authentication app")
|
|
314
|
+
init_parser.add_argument("--env", choices=["venv", "conda", "existing", "skip"],
|
|
315
|
+
help="Django: Python environment - create a venv/conda env, install into the "
|
|
316
|
+
"active one, or skip")
|
|
317
|
+
install_group = init_parser.add_mutually_exclusive_group()
|
|
318
|
+
install_group.add_argument("--install", dest="install", action="store_true", default=None,
|
|
319
|
+
help="Next.js/React/Flutter: install dependencies (npm install / flutter pub get)")
|
|
320
|
+
install_group.add_argument("--no-install", dest="install", action="store_false",
|
|
321
|
+
help="Next.js/React/Flutter: don't install dependencies")
|
|
322
|
+
init_parser.add_argument("--yes", "-y", action="store_true",
|
|
323
|
+
help="Don't ask: use the options given and defaults for the rest "
|
|
324
|
+
"(requires --name and --technology)")
|
|
325
|
+
|
|
326
|
+
# Create command for apps
|
|
327
|
+
create_parser = subparsers.add_parser("create", help="Create new components")
|
|
328
|
+
create_subparsers = create_parser.add_subparsers(dest="create_type", help="Type of component to create")
|
|
329
|
+
|
|
330
|
+
# Create app subcommand
|
|
331
|
+
app_parser = create_subparsers.add_parser("app", help="Create new Django app with NTC structure")
|
|
332
|
+
app_parser.add_argument("app_name", help="Name of the app to create")
|
|
333
|
+
url_group = app_parser.add_mutually_exclusive_group()
|
|
334
|
+
url_group.add_argument("--no-url", action="store_true",
|
|
335
|
+
help="Don't add the app's URLs anywhere; include them yourself")
|
|
336
|
+
url_group.add_argument("--url-prefix", default=None,
|
|
337
|
+
help="Add the app's URLs at the root urlpatterns under this prefix "
|
|
338
|
+
"(e.g. 'api/v2/'). Default: the project's api_v1 list (/api/v1/<app>/)")
|
|
339
|
+
|
|
340
|
+
# Legacy DRF command for backward compatibility
|
|
341
|
+
drf_parser = subparsers.add_parser("drf", help="Django REST Framework commands (legacy)")
|
|
342
|
+
drf_subparsers = drf_parser.add_subparsers(dest="drf_action")
|
|
343
|
+
drf_init_parser = drf_subparsers.add_parser("init", help="Initialize DRF project")
|
|
344
|
+
drf_init_parser.add_argument("path", help="Base path to create the project")
|
|
345
|
+
drf_init_parser.add_argument("name", help="Project name")
|
|
346
|
+
|
|
347
|
+
# Composer command for full-stack project composition
|
|
348
|
+
composer_parser = subparsers.add_parser("compose", help="Compose a full-stack project with multiple components")
|
|
349
|
+
composer_parser.add_argument("project_name", help="Name of the project")
|
|
350
|
+
composer_parser.add_argument("--path", help="Base path to create the project (default: current directory)")
|
|
351
|
+
|
|
352
|
+
# Start services command
|
|
353
|
+
start_parser = subparsers.add_parser("start", help="Start all services defined in ntc.yml")
|
|
354
|
+
start_parser.add_argument("--path", help="Path to project root directory (default: current directory)")
|
|
355
|
+
|
|
356
|
+
# Doctor command for setup diagnostics
|
|
357
|
+
doctor_parser = subparsers.add_parser("doctor", help="Check setup and fix common issues")
|
|
358
|
+
|
|
359
|
+
# TUI command for interactive terminal UI
|
|
360
|
+
tui_parser = subparsers.add_parser("tui", help="Launch interactive terminal UI")
|
|
361
|
+
|
|
362
|
+
args = parser.parse_args()
|
|
363
|
+
|
|
364
|
+
if args.command == "init":
|
|
365
|
+
technology = args.technology or args.frontend or args.backend
|
|
366
|
+
technology = {"drf": "django", "laravel": "django"}.get(technology, technology)
|
|
367
|
+
if args.yes and not (args.name and technology):
|
|
368
|
+
print_error("--yes needs --name and --technology, so there is nothing left to ask.")
|
|
369
|
+
sys.exit(2)
|
|
370
|
+
from ntc.wizard import run as run_wizard
|
|
371
|
+
created = run_wizard(technology=technology, name=args.name, path=args.path, with_auth=args.with_auth,
|
|
372
|
+
env_choice=args.env, install=args.install, assume_yes=args.yes)
|
|
373
|
+
sys.exit(0 if created else 1)
|
|
374
|
+
elif args.command == "create" and args.create_type == "app":
|
|
375
|
+
if not is_valid_project_name(args.app_name):
|
|
376
|
+
print_error("App name may only contain letters, numbers, underscores, and hyphens!")
|
|
377
|
+
sys.exit(1)
|
|
378
|
+
from ntc.app_creator import create_app_structure
|
|
379
|
+
create_app_structure(args.app_name, add_urls=not args.no_url, url_prefix=args.url_prefix)
|
|
380
|
+
elif args.command == "drf" and args.drf_action == "init":
|
|
381
|
+
# Legacy support
|
|
382
|
+
if not is_valid_project_name(args.name):
|
|
383
|
+
print_error("Project name may only contain letters, numbers, underscores, and hyphens!")
|
|
384
|
+
sys.exit(1)
|
|
385
|
+
from ntc.templates.drf_structure.main import init_drf_project
|
|
386
|
+
init_drf_project(args.path, args.name)
|
|
387
|
+
elif args.command == "compose":
|
|
388
|
+
# Composer for full-stack project composition
|
|
389
|
+
if not is_valid_project_name(args.project_name):
|
|
390
|
+
print_error("Project name may only contain letters, numbers, underscores, and hyphens!")
|
|
391
|
+
sys.exit(1)
|
|
392
|
+
from ntc.composer.main import compose_project
|
|
393
|
+
if args.path:
|
|
394
|
+
compose_project(args.project_name, args.path)
|
|
395
|
+
else:
|
|
396
|
+
compose_project(args.project_name)
|
|
397
|
+
elif args.command == "start":
|
|
398
|
+
# Start services from ntc.yml configuration
|
|
399
|
+
from ntc.composer.main import start_services
|
|
400
|
+
if args.path:
|
|
401
|
+
start_services(args.path)
|
|
402
|
+
else:
|
|
403
|
+
start_services()
|
|
404
|
+
elif args.command == "doctor":
|
|
405
|
+
run_doctor()
|
|
406
|
+
elif args.command == "tui":
|
|
407
|
+
from ntc.ui.app import run_tui
|
|
408
|
+
run_tui()
|
|
409
|
+
else:
|
|
410
|
+
# Show help if no command provided
|
|
411
|
+
print_banner()
|
|
412
|
+
print(f"\n {Fore.LIGHTBLACK_EX}Quick Start:{Style.RESET_ALL}")
|
|
413
|
+
print(f" {Fore.WHITE}ntc tui{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}interactive terminal UI{Style.RESET_ALL}")
|
|
414
|
+
print(f" {Fore.WHITE}ntc init{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}interactive setup{Style.RESET_ALL}")
|
|
415
|
+
print(f" {Fore.WHITE}ntc init -t django{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}Django project{Style.RESET_ALL}")
|
|
416
|
+
print(f" {Fore.WHITE}ntc doctor{Style.RESET_ALL} {Fore.LIGHTBLACK_EX}check setup{Style.RESET_ALL}")
|
|
417
|
+
print()
|
|
418
|
+
parser.print_help()
|
|
419
|
+
|
|
420
|
+
if __name__ == "__main__":
|
|
421
|
+
main()
|
|
@@ -37,8 +37,8 @@ modules_to_register = [
|
|
|
37
37
|
("ntc.templates.flutter", os.path.join(this_dir, "templates", "flutter", "__init__.py")),
|
|
38
38
|
("ntc.templates.flutter.main", os.path.join(this_dir, "templates", "flutter", "main.py")),
|
|
39
39
|
("ntc.templates.auth_app", os.path.join(this_dir, "templates", "auth_app", "__init__.py")),
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
# Not ntc.ui / ntc.ui.app: they import textual (most of a second), and `ntc tui` imports
|
|
41
|
+
# them itself when it runs.
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
for modname, file_path in modules_to_register:
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Generated by Django 5.2.7 on 2026-10-20 16:26
|
|
2
|
+
|
|
3
|
+
import apps.authentication.models
|
|
4
|
+
import django.contrib.auth.validators
|
|
5
|
+
import django.db.models.functions.text
|
|
6
|
+
import django.utils.timezone
|
|
7
|
+
from django.db import migrations, models
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Migration(migrations.Migration):
|
|
11
|
+
|
|
12
|
+
initial = True
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
('auth', '0012_alter_user_first_name_max_length'),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
operations = [
|
|
19
|
+
migrations.CreateModel(
|
|
20
|
+
name='User',
|
|
21
|
+
fields=[
|
|
22
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
23
|
+
('password', models.CharField(max_length=128, verbose_name='password')),
|
|
24
|
+
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
|
25
|
+
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
|
26
|
+
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
|
27
|
+
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
|
28
|
+
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
|
29
|
+
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
|
30
|
+
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
|
31
|
+
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
|
32
|
+
('email', models.EmailField(max_length=254, unique=True)),
|
|
33
|
+
('phone_number', models.CharField(blank=True, max_length=15)),
|
|
34
|
+
('date_of_birth', models.DateField(blank=True, null=True)),
|
|
35
|
+
('profile_picture', models.ImageField(blank=True, null=True, upload_to='profile_pics/')),
|
|
36
|
+
('is_verified', models.BooleanField(default=False)),
|
|
37
|
+
('created_at', models.DateTimeField(auto_now_add=True)),
|
|
38
|
+
('updated_at', models.DateTimeField(auto_now=True)),
|
|
39
|
+
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
|
40
|
+
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
|
41
|
+
],
|
|
42
|
+
options={
|
|
43
|
+
'verbose_name': 'User',
|
|
44
|
+
'verbose_name_plural': 'Users',
|
|
45
|
+
'constraints': [models.UniqueConstraint(django.db.models.functions.text.Lower('email'), name='authentication_user_email_ci_unique')],
|
|
46
|
+
},
|
|
47
|
+
managers=[
|
|
48
|
+
('objects', apps.authentication.models.UserManager()),
|
|
49
|
+
],
|
|
50
|
+
),
|
|
51
|
+
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from django.contrib.auth.models import AbstractUser, UserManager as DjangoUserManager
|
|
2
|
+
from django.db import models
|
|
3
|
+
from django.db.models.functions import Lower
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def normalize_email(email):
|
|
7
|
+
"""Emails are stored lowercase: A@x.com and a@x.com are the same person, and sign-in and
|
|
8
|
+
password resets look them up exactly."""
|
|
9
|
+
return (email or '').strip().lower()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UserManager(DjangoUserManager):
|
|
13
|
+
def _create_user(self, username, email, password, **extra_fields):
|
|
14
|
+
return super()._create_user(username, normalize_email(email), password, **extra_fields)
|
|
15
|
+
|
|
16
|
+
def get_by_natural_key(self, username):
|
|
17
|
+
# USERNAME_FIELD is the email: sign-in matches it whatever the case typed.
|
|
18
|
+
return self.get(email=normalize_email(username))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class User(AbstractUser):
|
|
22
|
+
"""Custom User model extending AbstractUser. first_name/last_name keep AbstractUser's 150 characters."""
|
|
23
|
+
email = models.EmailField(unique=True)
|
|
24
|
+
phone_number = models.CharField(max_length=15, blank=True)
|
|
25
|
+
date_of_birth = models.DateField(null=True, blank=True)
|
|
26
|
+
# Stored in R2 when it's configured (STORAGES in settings), on disk in development.
|
|
27
|
+
profile_picture = models.ImageField(upload_to='profile_pics/', null=True, blank=True)
|
|
28
|
+
is_verified = models.BooleanField(default=False)
|
|
29
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
30
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
31
|
+
|
|
32
|
+
objects = UserManager()
|
|
33
|
+
|
|
34
|
+
USERNAME_FIELD = 'email'
|
|
35
|
+
REQUIRED_FIELDS = ['username']
|
|
36
|
+
|
|
37
|
+
class Meta:
|
|
38
|
+
verbose_name = 'User'
|
|
39
|
+
verbose_name_plural = 'Users'
|
|
40
|
+
constraints = [
|
|
41
|
+
# Belt and braces with the lowercase normalization: no two accounts differ only by case.
|
|
42
|
+
models.UniqueConstraint(Lower('email'), name='authentication_user_email_ci_unique'),
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
def save(self, *args, **kwargs):
|
|
46
|
+
self.email = normalize_email(self.email)
|
|
47
|
+
super().save(*args, **kwargs)
|
|
48
|
+
|
|
49
|
+
def __str__(self):
|
|
50
|
+
return self.email
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def full_name(self) -> str:
|
|
54
|
+
return f"{self.first_name} {self.last_name}".strip()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from rest_framework import serializers
|
|
2
2
|
from django.contrib.auth import authenticate
|
|
3
3
|
from django.contrib.auth.password_validation import validate_password
|
|
4
|
-
from .models import User
|
|
4
|
+
from .models import User, normalize_email
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class UserRegistrationSerializer(serializers.ModelSerializer):
|
|
@@ -13,6 +13,13 @@ class UserRegistrationSerializer(serializers.ModelSerializer):
|
|
|
13
13
|
model = User
|
|
14
14
|
fields = ('email', 'username', 'password', 'password_confirm', 'first_name', 'last_name')
|
|
15
15
|
|
|
16
|
+
def validate_email(self, value):
|
|
17
|
+
# Case-insensitive, like the database constraint: A@x.com is already taken by a@x.com.
|
|
18
|
+
value = normalize_email(value)
|
|
19
|
+
if User.objects.filter(email=value).exists():
|
|
20
|
+
raise serializers.ValidationError('An account with this email already exists.')
|
|
21
|
+
return value
|
|
22
|
+
|
|
16
23
|
def validate(self, attrs):
|
|
17
24
|
if attrs['password'] != attrs['password_confirm']:
|
|
18
25
|
raise serializers.ValidationError({'password_confirm': "Passwords don't match."})
|