fastapi-fullstack 0.1.3__tar.gz → 0.1.4__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.
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/PKG-INFO +15 -8
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/README.md +14 -7
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/pyproject.toml +1 -1
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/README.md +78 -1
- fastapi_fullstack-0.1.4/template/{{cookiecutter.project_slug}}/backend/app/admin.py +447 -0
- fastapi_fullstack-0.1.4/template/{{cookiecutter.project_slug}}/backend/tests/test_admin.py +890 -0
- fastapi_fullstack-0.1.3/template/{{cookiecutter.project_slug}}/backend/app/admin.py +0 -356
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/.gitignore +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/LICENSE +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/fastapi_gen/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/fastapi_gen/cli.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/fastapi_gen/config.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/fastapi_gen/generator.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/fastapi_gen/prompts.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/cookiecutter.json +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/hooks/post_gen_project.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/.env.example +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/.gitignore +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/CLAUDE.md +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/Makefile +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/.dockerignore +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/Dockerfile +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/alembic.ini +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/main.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/docker-compose.yml +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/.env.example +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/.gitignore +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/README.md +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/package.json +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/layout.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/login/page.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/register/page.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/chat/page.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/dashboard/page.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/layout.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/profile/page.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/auth/callback/page.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/page.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +0 -0
- {fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-fullstack
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Interactive FastAPI project generator with Logfire observability
|
|
5
5
|
Project-URL: Homepage, https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template
|
|
6
6
|
Project-URL: Documentation, https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template#readme
|
|
@@ -40,6 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
<a href="https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template/blob/main/LICENSE"><img src="https://img.shields.io/github/license/vstorm-co/full-stack-fastapi-nextjs-llm-template?color=blue" alt="License"></a>
|
|
41
41
|
<a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue?logo=python&logoColor=white" alt="Python"></a>
|
|
42
42
|
<a href="https://pypi.org/project/fastapi-fullstack/"><img src="https://img.shields.io/pypi/v/fastapi-fullstack?color=green&logo=pypi&logoColor=white" alt="PyPI"></a>
|
|
43
|
+
<img src="https://img.shields.io/badge/coverage-100%25-brightgreen" alt="Coverage">
|
|
43
44
|
<img src="https://img.shields.io/badge/integrations-20%2B-brightgreen" alt="20+ Integrations">
|
|
44
45
|
</p>
|
|
45
46
|
|
|
@@ -59,6 +60,12 @@ Description-Content-Type: text/markdown
|
|
|
59
60
|
<a href="#-documentation">Documentation</a>
|
|
60
61
|
</p>
|
|
61
62
|
|
|
63
|
+
## Related Projects
|
|
64
|
+
|
|
65
|
+
> **Building advanced AI agents?** Check out [pydantic-deep](https://github.com/vstorm-co/pydantic-deepagents) - a deep agent framework built on pydantic-ai with planning, filesystem, and subagent capabilities.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
62
69
|
## 🎯 Why This Template
|
|
63
70
|
|
|
64
71
|
Building AI/LLM applications requires more than just an API wrapper. You need:
|
|
@@ -121,18 +128,18 @@ This template gives you all of that out of the box, with **20+ configurable inte
|
|
|
121
128
|
## 🎬 Demo
|
|
122
129
|
|
|
123
130
|
<p align="center">
|
|
124
|
-
<img src="assets/app_start.gif" alt="FastAPI Fullstack Generator Demo"
|
|
131
|
+
<img src="assets/app_start.gif" alt="FastAPI Fullstack Generator Demo">
|
|
125
132
|
</p>
|
|
126
133
|
|
|
127
134
|
### Screenshots
|
|
128
135
|
|
|
129
136
|
<p align="center">
|
|
130
|
-
<img src="assets/docs_2.png" alt="API Documentation"
|
|
131
|
-
<img src="assets/admin.png" alt="Admin Panel"
|
|
132
|
-
<img src="assets/chat_view_light.png" alt="Chat Interface - Light Mode"
|
|
133
|
-
<img src="assets/chat_view_dark.png" alt="Chat Interface - Dark Mode"
|
|
134
|
-
<img src="assets/login.png" alt="Login Page"
|
|
135
|
-
<img src="assets/register.png" alt="Register Page"
|
|
137
|
+
<img src="assets/docs_2.png" alt="API Documentation"><br><br>
|
|
138
|
+
<img src="assets/admin.png" alt="Admin Panel"><br><br>
|
|
139
|
+
<img src="assets/chat_view_light.png" alt="Chat Interface - Light Mode"><br><br>
|
|
140
|
+
<img src="assets/chat_view_dark.png" alt="Chat Interface - Dark Mode"><br><br>
|
|
141
|
+
<img src="assets/login.png" alt="Login Page"><br><br>
|
|
142
|
+
<img src="assets/register.png" alt="Register Page">
|
|
136
143
|
</p>
|
|
137
144
|
|
|
138
145
|
---
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<a href="https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template/blob/main/LICENSE"><img src="https://img.shields.io/github/license/vstorm-co/full-stack-fastapi-nextjs-llm-template?color=blue" alt="License"></a>
|
|
6
6
|
<a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue?logo=python&logoColor=white" alt="Python"></a>
|
|
7
7
|
<a href="https://pypi.org/project/fastapi-fullstack/"><img src="https://img.shields.io/pypi/v/fastapi-fullstack?color=green&logo=pypi&logoColor=white" alt="PyPI"></a>
|
|
8
|
+
<img src="https://img.shields.io/badge/coverage-100%25-brightgreen" alt="Coverage">
|
|
8
9
|
<img src="https://img.shields.io/badge/integrations-20%2B-brightgreen" alt="20+ Integrations">
|
|
9
10
|
</p>
|
|
10
11
|
|
|
@@ -24,6 +25,12 @@
|
|
|
24
25
|
<a href="#-documentation">Documentation</a>
|
|
25
26
|
</p>
|
|
26
27
|
|
|
28
|
+
## Related Projects
|
|
29
|
+
|
|
30
|
+
> **Building advanced AI agents?** Check out [pydantic-deep](https://github.com/vstorm-co/pydantic-deepagents) - a deep agent framework built on pydantic-ai with planning, filesystem, and subagent capabilities.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
27
34
|
## 🎯 Why This Template
|
|
28
35
|
|
|
29
36
|
Building AI/LLM applications requires more than just an API wrapper. You need:
|
|
@@ -86,18 +93,18 @@ This template gives you all of that out of the box, with **20+ configurable inte
|
|
|
86
93
|
## 🎬 Demo
|
|
87
94
|
|
|
88
95
|
<p align="center">
|
|
89
|
-
<img src="assets/app_start.gif" alt="FastAPI Fullstack Generator Demo"
|
|
96
|
+
<img src="assets/app_start.gif" alt="FastAPI Fullstack Generator Demo">
|
|
90
97
|
</p>
|
|
91
98
|
|
|
92
99
|
### Screenshots
|
|
93
100
|
|
|
94
101
|
<p align="center">
|
|
95
|
-
<img src="assets/docs_2.png" alt="API Documentation"
|
|
96
|
-
<img src="assets/admin.png" alt="Admin Panel"
|
|
97
|
-
<img src="assets/chat_view_light.png" alt="Chat Interface - Light Mode"
|
|
98
|
-
<img src="assets/chat_view_dark.png" alt="Chat Interface - Dark Mode"
|
|
99
|
-
<img src="assets/login.png" alt="Login Page"
|
|
100
|
-
<img src="assets/register.png" alt="Register Page"
|
|
102
|
+
<img src="assets/docs_2.png" alt="API Documentation"><br><br>
|
|
103
|
+
<img src="assets/admin.png" alt="Admin Panel"><br><br>
|
|
104
|
+
<img src="assets/chat_view_light.png" alt="Chat Interface - Light Mode"><br><br>
|
|
105
|
+
<img src="assets/chat_view_dark.png" alt="Chat Interface - Dark Mode"><br><br>
|
|
106
|
+
<img src="assets/login.png" alt="Login Page"><br><br>
|
|
107
|
+
<img src="assets/register.png" alt="Register Page">
|
|
101
108
|
</p>
|
|
102
109
|
|
|
103
110
|
---
|
{fastapi_fullstack-0.1.3 → fastapi_fullstack-0.1.4}/template/{{cookiecutter.project_slug}}/README.md
RENAMED
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
- 🚦 **Rate Limiting** - Request throttling
|
|
59
59
|
{%- endif %}
|
|
60
60
|
{%- if cookiecutter.enable_admin_panel %}
|
|
61
|
-
- 🗄️ **Admin Panel** - SQLAdmin
|
|
61
|
+
- 🗄️ **Admin Panel** - SQLAdmin with automatic model discovery
|
|
62
62
|
{%- endif %}
|
|
63
63
|
{%- if cookiecutter.enable_logfire %}
|
|
64
64
|
- 📊 **Logfire** - Full-stack observability (see [Logfire section](#logfire-observability))
|
|
@@ -378,6 +378,9 @@ def seed_database(count: int):
|
|
|
378
378
|
│ │ ├── db/
|
|
379
379
|
│ │ │ ├── models/ # Database models
|
|
380
380
|
│ │ │ └── session.py # Connection management
|
|
381
|
+
{%- if cookiecutter.enable_admin_panel %}
|
|
382
|
+
│ │ ├── admin.py # SQLAdmin with auto-discovery
|
|
383
|
+
{%- endif %}
|
|
381
384
|
│ │ ├── schemas/ # Pydantic schemas
|
|
382
385
|
│ │ ├── repositories/ # Data access layer
|
|
383
386
|
│ │ ├── services/ # Business logic
|
|
@@ -432,6 +435,80 @@ API Routes → Services → Repositories → Database
|
|
|
432
435
|
> 📚 For detailed architecture documentation, see the [template repository](https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template/blob/main/docs/architecture.md).
|
|
433
436
|
|
|
434
437
|
---
|
|
438
|
+
{%- if cookiecutter.enable_admin_panel %}
|
|
439
|
+
|
|
440
|
+
## Admin Panel
|
|
441
|
+
|
|
442
|
+
The admin panel provides a web-based interface for managing database records. It uses [SQLAdmin](https://aminalaee.dev/sqladmin/) with **automatic model discovery** - all SQLAlchemy models are automatically registered without manual configuration.
|
|
443
|
+
|
|
444
|
+
### Access
|
|
445
|
+
|
|
446
|
+
- URL: `http://localhost:{{ cookiecutter.backend_port }}/admin`
|
|
447
|
+
{%- if cookiecutter.admin_require_auth %}
|
|
448
|
+
- **Authentication required**: Login with superuser credentials
|
|
449
|
+
{%- endif %}
|
|
450
|
+
|
|
451
|
+
### Features
|
|
452
|
+
|
|
453
|
+
| Feature | Description |
|
|
454
|
+
|---------|-------------|
|
|
455
|
+
| **Auto-Discovery** | All models from `Base.registry` are automatically registered |
|
|
456
|
+
| **Smart Defaults** | Searchable columns (String types), sortable columns, form exclusions |
|
|
457
|
+
| **Sensitive Data Protection** | Password, token, secret fields auto-excluded from forms |
|
|
458
|
+
| **Custom Overrides** | Per-model configuration in `CUSTOM_MODEL_CONFIGS` |
|
|
459
|
+
|
|
460
|
+
### Customizing Model Views
|
|
461
|
+
|
|
462
|
+
To customize a model's admin view, add it to `CUSTOM_MODEL_CONFIGS` in `app/admin.py`:
|
|
463
|
+
|
|
464
|
+
```python
|
|
465
|
+
CUSTOM_MODEL_CONFIGS: dict[type, dict[str, Any]] = {
|
|
466
|
+
User: {
|
|
467
|
+
"icon": "fa-solid fa-user",
|
|
468
|
+
"form_excluded_columns": [User.hashed_password],
|
|
469
|
+
"can_delete": False, # Prevent deletion
|
|
470
|
+
},
|
|
471
|
+
Order: {
|
|
472
|
+
"name": "Customer Order",
|
|
473
|
+
"name_plural": "Customer Orders",
|
|
474
|
+
"column_list": [Order.id, Order.status, Order.created_at],
|
|
475
|
+
"can_create": False, # Read-only
|
|
476
|
+
},
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Available Options
|
|
481
|
+
|
|
482
|
+
| Option | Type | Description |
|
|
483
|
+
|--------|------|-------------|
|
|
484
|
+
| `name` | `str` | Display name in admin |
|
|
485
|
+
| `name_plural` | `str` | Plural name for list view |
|
|
486
|
+
| `icon` | `str` | Font Awesome icon class |
|
|
487
|
+
| `column_list` | `list` | Columns to show in list view |
|
|
488
|
+
| `column_searchable_list` | `list` | Columns to enable search |
|
|
489
|
+
| `column_sortable_list` | `list` | Columns to enable sorting |
|
|
490
|
+
| `form_excluded_columns` | `list` | Columns to hide in forms |
|
|
491
|
+
| `can_create` | `bool` | Allow creating records |
|
|
492
|
+
| `can_edit` | `bool` | Allow editing records |
|
|
493
|
+
| `can_delete` | `bool` | Allow deleting records |
|
|
494
|
+
| `can_view_details` | `bool` | Allow viewing record details |
|
|
495
|
+
|
|
496
|
+
### Excluding Models
|
|
497
|
+
|
|
498
|
+
To exclude a model from auto-registration:
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
# In app/admin.py setup_admin()
|
|
502
|
+
register_models_auto(
|
|
503
|
+
admin,
|
|
504
|
+
Base,
|
|
505
|
+
exclude_models=[InternalLog, TempData], # These won't appear in admin
|
|
506
|
+
custom_configs=CUSTOM_MODEL_CONFIGS,
|
|
507
|
+
)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
{%- endif %}
|
|
435
512
|
|
|
436
513
|
## Configuration
|
|
437
514
|
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_admin_panel and cookiecutter.use_postgresql %}
|
|
2
|
+
"""SQLAdmin configuration with automatic model discovery."""
|
|
3
|
+
|
|
4
|
+
from typing import Any, ClassVar
|
|
5
|
+
|
|
6
|
+
from sqlalchemy import String, inspect
|
|
7
|
+
from sqlalchemy.engine import Engine
|
|
8
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
9
|
+
from sqladmin import Admin, ModelView
|
|
10
|
+
{%- if cookiecutter.admin_require_auth %}
|
|
11
|
+
from sqladmin.authentication import AuthenticationBackend
|
|
12
|
+
from starlette.requests import Request
|
|
13
|
+
{%- endif %}
|
|
14
|
+
|
|
15
|
+
from app.core.config import settings
|
|
16
|
+
{%- if cookiecutter.admin_require_auth %}
|
|
17
|
+
from app.core.security import verify_password
|
|
18
|
+
{%- endif %}
|
|
19
|
+
from app.db.base import Base
|
|
20
|
+
from app.db.models.user import User
|
|
21
|
+
{%- if cookiecutter.enable_session_management %}
|
|
22
|
+
from app.db.models.session import Session
|
|
23
|
+
{%- endif %}
|
|
24
|
+
{%- if cookiecutter.include_example_crud %}
|
|
25
|
+
from app.db.models.item import Item
|
|
26
|
+
{%- endif %}
|
|
27
|
+
{%- if cookiecutter.enable_conversation_persistence %}
|
|
28
|
+
from app.db.models.conversation import Conversation, Message, ToolCall
|
|
29
|
+
{%- endif %}
|
|
30
|
+
{%- if cookiecutter.enable_webhooks %}
|
|
31
|
+
from app.db.models.webhook import Webhook, WebhookDelivery
|
|
32
|
+
{%- endif %}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Columns that should be excluded from forms (sensitive data)
|
|
36
|
+
SENSITIVE_COLUMN_PATTERNS: list[str] = [
|
|
37
|
+
"password",
|
|
38
|
+
"hashed_password",
|
|
39
|
+
"secret",
|
|
40
|
+
"token",
|
|
41
|
+
"api_key",
|
|
42
|
+
"refresh_token",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# Columns that should be searchable by default (string columns)
|
|
46
|
+
SEARCHABLE_COLUMN_TYPES: tuple[type, ...] = (String,)
|
|
47
|
+
|
|
48
|
+
# Columns that are auto-generated and should be excluded from create/edit forms
|
|
49
|
+
AUTO_GENERATED_COLUMNS: list[str] = [
|
|
50
|
+
"created_at",
|
|
51
|
+
"updated_at",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
# Model icons mapping (model name -> Font Awesome icon)
|
|
55
|
+
MODEL_ICONS: dict[str, str] = {
|
|
56
|
+
"User": "fa-solid fa-user",
|
|
57
|
+
"Session": "fa-solid fa-key",
|
|
58
|
+
"Item": "fa-solid fa-box",
|
|
59
|
+
"Conversation": "fa-solid fa-comments",
|
|
60
|
+
"Message": "fa-solid fa-message",
|
|
61
|
+
"ToolCall": "fa-solid fa-wrench",
|
|
62
|
+
"Webhook": "fa-solid fa-link",
|
|
63
|
+
"WebhookDelivery": "fa-solid fa-paper-plane",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def discover_models(base: type[DeclarativeBase]) -> list[type]:
|
|
68
|
+
"""Discover all SQLAlchemy models registered with the given Base.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
base: The SQLAlchemy DeclarativeBase class.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
List of model classes that inherit from the Base.
|
|
75
|
+
"""
|
|
76
|
+
return [mapper.class_ for mapper in base.registry.mappers]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def get_model_columns(model: type) -> list[str]:
|
|
80
|
+
"""Get all column names from a SQLAlchemy model.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
model: The SQLAlchemy model class.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
List of column names.
|
|
87
|
+
"""
|
|
88
|
+
mapper = inspect(model)
|
|
89
|
+
return [column.key for column in mapper.columns]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_searchable_columns(model: type) -> list[str]:
|
|
93
|
+
"""Get columns suitable for searching (String type columns).
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
model: The SQLAlchemy model class.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
List of searchable column names.
|
|
100
|
+
"""
|
|
101
|
+
mapper = inspect(model)
|
|
102
|
+
searchable = []
|
|
103
|
+
for column in mapper.columns:
|
|
104
|
+
# Include String columns that are not sensitive
|
|
105
|
+
is_searchable_type = isinstance(column.type, SEARCHABLE_COLUMN_TYPES)
|
|
106
|
+
is_sensitive = any(pattern in column.key.lower() for pattern in SENSITIVE_COLUMN_PATTERNS)
|
|
107
|
+
if is_searchable_type and not is_sensitive:
|
|
108
|
+
searchable.append(column.key)
|
|
109
|
+
return searchable
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_sortable_columns(model: type) -> list[str]:
|
|
113
|
+
"""Get columns suitable for sorting.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
model: The SQLAlchemy model class.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
List of sortable column names.
|
|
120
|
+
"""
|
|
121
|
+
mapper = inspect(model)
|
|
122
|
+
return [column.key for column in mapper.columns]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get_form_excluded_columns(model: type) -> list[str]:
|
|
126
|
+
"""Get columns that should be excluded from create/edit forms.
|
|
127
|
+
|
|
128
|
+
Excludes sensitive columns and auto-generated columns.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
model: The SQLAlchemy model class.
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
List of column names to exclude from forms.
|
|
135
|
+
"""
|
|
136
|
+
excluded = []
|
|
137
|
+
for column_name in get_model_columns(model):
|
|
138
|
+
# Exclude sensitive columns
|
|
139
|
+
if any(pattern in column_name.lower() for pattern in SENSITIVE_COLUMN_PATTERNS):
|
|
140
|
+
excluded.append(column_name)
|
|
141
|
+
# Exclude auto-generated columns
|
|
142
|
+
elif column_name in AUTO_GENERATED_COLUMNS:
|
|
143
|
+
excluded.append(column_name)
|
|
144
|
+
return excluded
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def pluralize(name: str) -> str:
|
|
148
|
+
"""Simple pluralization for model names.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
name: Singular name.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Pluralized name.
|
|
155
|
+
"""
|
|
156
|
+
if name.endswith("y"):
|
|
157
|
+
return name[:-1] + "ies"
|
|
158
|
+
elif name.endswith("s") or name.endswith("x") or name.endswith("ch") or name.endswith("sh"):
|
|
159
|
+
return name + "es"
|
|
160
|
+
return name + "s"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def create_model_admin(
|
|
164
|
+
model: type,
|
|
165
|
+
*,
|
|
166
|
+
name: str | None = None,
|
|
167
|
+
name_plural: str | None = None,
|
|
168
|
+
icon: str | None = None,
|
|
169
|
+
column_list: list[Any] | None = None,
|
|
170
|
+
column_searchable_list: list[Any] | None = None,
|
|
171
|
+
column_sortable_list: list[Any] | None = None,
|
|
172
|
+
form_excluded_columns: list[Any] | None = None,
|
|
173
|
+
can_create: bool = True,
|
|
174
|
+
can_edit: bool = True,
|
|
175
|
+
can_delete: bool = True,
|
|
176
|
+
can_view_details: bool = True,
|
|
177
|
+
) -> type[ModelView]:
|
|
178
|
+
"""Dynamically create a ModelView class for a SQLAlchemy model.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
model: The SQLAlchemy model class.
|
|
182
|
+
name: Display name (defaults to model class name).
|
|
183
|
+
name_plural: Plural display name (defaults to auto-pluralized name).
|
|
184
|
+
icon: Font Awesome icon class.
|
|
185
|
+
column_list: Columns to display in list view.
|
|
186
|
+
column_searchable_list: Columns to enable search on.
|
|
187
|
+
column_sortable_list: Columns to enable sorting on.
|
|
188
|
+
form_excluded_columns: Columns to exclude from forms.
|
|
189
|
+
can_create: Allow creating new records.
|
|
190
|
+
can_edit: Allow editing records.
|
|
191
|
+
can_delete: Allow deleting records.
|
|
192
|
+
can_view_details: Allow viewing record details.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
A dynamically created ModelView subclass.
|
|
196
|
+
"""
|
|
197
|
+
import types
|
|
198
|
+
|
|
199
|
+
model_name = model.__name__
|
|
200
|
+
|
|
201
|
+
# Use provided values or generate defaults
|
|
202
|
+
_name = name or model_name
|
|
203
|
+
_name_plural = name_plural or pluralize(_name)
|
|
204
|
+
_icon = icon or MODEL_ICONS.get(model_name, "fa-solid fa-database")
|
|
205
|
+
|
|
206
|
+
# Get column attributes from the model
|
|
207
|
+
_column_list = column_list
|
|
208
|
+
if _column_list is None:
|
|
209
|
+
columns = get_model_columns(model)
|
|
210
|
+
_column_list = [getattr(model, col) for col in columns if hasattr(model, col)]
|
|
211
|
+
|
|
212
|
+
_column_searchable_list = column_searchable_list
|
|
213
|
+
if _column_searchable_list is None:
|
|
214
|
+
searchable = get_searchable_columns(model)
|
|
215
|
+
_column_searchable_list = [getattr(model, col) for col in searchable if hasattr(model, col)]
|
|
216
|
+
|
|
217
|
+
_column_sortable_list = column_sortable_list
|
|
218
|
+
if _column_sortable_list is None:
|
|
219
|
+
sortable = get_sortable_columns(model)
|
|
220
|
+
_column_sortable_list = [getattr(model, col) for col in sortable if hasattr(model, col)]
|
|
221
|
+
|
|
222
|
+
_form_excluded_columns = form_excluded_columns
|
|
223
|
+
if _form_excluded_columns is None:
|
|
224
|
+
excluded = get_form_excluded_columns(model)
|
|
225
|
+
_form_excluded_columns = [getattr(model, col) for col in excluded if hasattr(model, col)]
|
|
226
|
+
|
|
227
|
+
# Create class attributes in the exec_body callback
|
|
228
|
+
def exec_body(ns: dict[str, Any]) -> None:
|
|
229
|
+
ns["name"] = _name
|
|
230
|
+
ns["name_plural"] = _name_plural
|
|
231
|
+
ns["icon"] = _icon
|
|
232
|
+
ns["column_list"] = _column_list
|
|
233
|
+
ns["column_searchable_list"] = _column_searchable_list
|
|
234
|
+
ns["column_sortable_list"] = _column_sortable_list
|
|
235
|
+
ns["form_excluded_columns"] = _form_excluded_columns
|
|
236
|
+
ns["can_create"] = can_create
|
|
237
|
+
ns["can_edit"] = can_edit
|
|
238
|
+
ns["can_delete"] = can_delete
|
|
239
|
+
ns["can_view_details"] = can_view_details
|
|
240
|
+
# Add ClassVar type hints for sqladmin compatibility
|
|
241
|
+
ns["__annotations__"] = {
|
|
242
|
+
"column_list": ClassVar,
|
|
243
|
+
"column_searchable_list": ClassVar,
|
|
244
|
+
"column_sortable_list": ClassVar,
|
|
245
|
+
"form_excluded_columns": ClassVar,
|
|
246
|
+
"can_create": ClassVar,
|
|
247
|
+
"can_edit": ClassVar,
|
|
248
|
+
"can_delete": ClassVar,
|
|
249
|
+
"can_view_details": ClassVar,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# Create the class using types.new_class to properly pass model kwarg to metaclass
|
|
253
|
+
class_name = f"{model_name}Admin"
|
|
254
|
+
admin_class = types.new_class(
|
|
255
|
+
class_name,
|
|
256
|
+
(ModelView,),
|
|
257
|
+
{"model": model}, # Pass model to metaclass
|
|
258
|
+
exec_body,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
return admin_class # type: ignore[return-value]
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def register_models_auto(
|
|
265
|
+
admin: Admin,
|
|
266
|
+
base: type[DeclarativeBase],
|
|
267
|
+
*,
|
|
268
|
+
exclude_models: list[type] | None = None,
|
|
269
|
+
custom_configs: dict[type, dict[str, Any]] | None = None,
|
|
270
|
+
) -> list[type[ModelView]]:
|
|
271
|
+
"""Auto-discover and register all models with the admin panel.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
admin: The SQLAdmin instance.
|
|
275
|
+
base: The SQLAlchemy DeclarativeBase class.
|
|
276
|
+
exclude_models: Models to exclude from auto-registration.
|
|
277
|
+
custom_configs: Custom configuration overrides per model.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
List of registered ModelView classes.
|
|
281
|
+
"""
|
|
282
|
+
exclude_models = exclude_models or []
|
|
283
|
+
custom_configs = custom_configs or {}
|
|
284
|
+
|
|
285
|
+
registered_views: list[type[ModelView]] = []
|
|
286
|
+
models = discover_models(base)
|
|
287
|
+
|
|
288
|
+
for model in models:
|
|
289
|
+
if model in exclude_models:
|
|
290
|
+
continue
|
|
291
|
+
|
|
292
|
+
# Get custom config for this model if provided
|
|
293
|
+
config = custom_configs.get(model, {})
|
|
294
|
+
|
|
295
|
+
# Create and register the admin view
|
|
296
|
+
admin_class = create_model_admin(model, **config)
|
|
297
|
+
admin.add_view(admin_class)
|
|
298
|
+
registered_views.append(admin_class)
|
|
299
|
+
|
|
300
|
+
return registered_views
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# SQLAdmin requires a synchronous engine
|
|
304
|
+
_sync_engine: Engine | None = None
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def get_sync_engine() -> Engine:
|
|
308
|
+
"""Get or create the synchronous engine for SQLAdmin."""
|
|
309
|
+
global _sync_engine
|
|
310
|
+
if _sync_engine is None:
|
|
311
|
+
from sqlalchemy import create_engine
|
|
312
|
+
|
|
313
|
+
_sync_engine = create_engine(settings.DATABASE_URL_SYNC, echo=settings.DEBUG)
|
|
314
|
+
return _sync_engine
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
{%- if cookiecutter.admin_require_auth %}
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class AdminAuth(AuthenticationBackend):
|
|
321
|
+
"""Admin panel authentication backend.
|
|
322
|
+
|
|
323
|
+
Requires superuser credentials to access the admin panel.
|
|
324
|
+
"""
|
|
325
|
+
|
|
326
|
+
async def login(self, request: Request) -> bool:
|
|
327
|
+
"""Validate admin login credentials."""
|
|
328
|
+
form = await request.form()
|
|
329
|
+
email = form.get("username")
|
|
330
|
+
password = form.get("password")
|
|
331
|
+
|
|
332
|
+
if not email or not password:
|
|
333
|
+
return False
|
|
334
|
+
|
|
335
|
+
# Get user from database
|
|
336
|
+
from sqlalchemy.orm import Session as DBSession
|
|
337
|
+
|
|
338
|
+
with DBSession(get_sync_engine()) as session:
|
|
339
|
+
user = session.query(User).filter(User.email == email).first()
|
|
340
|
+
|
|
341
|
+
if (
|
|
342
|
+
user
|
|
343
|
+
and verify_password(str(password), user.hashed_password)
|
|
344
|
+
and user.is_superuser
|
|
345
|
+
):
|
|
346
|
+
# Store user info in session
|
|
347
|
+
request.session["admin_user_id"] = str(user.id)
|
|
348
|
+
request.session["admin_email"] = user.email
|
|
349
|
+
return True
|
|
350
|
+
|
|
351
|
+
return False
|
|
352
|
+
|
|
353
|
+
async def logout(self, request: Request) -> bool:
|
|
354
|
+
"""Clear admin session."""
|
|
355
|
+
request.session.clear()
|
|
356
|
+
return True
|
|
357
|
+
|
|
358
|
+
async def authenticate(self, request: Request) -> bool:
|
|
359
|
+
"""Check if user is authenticated."""
|
|
360
|
+
admin_user_id = request.session.get("admin_user_id")
|
|
361
|
+
if not admin_user_id:
|
|
362
|
+
return False
|
|
363
|
+
|
|
364
|
+
# Verify user still exists and is superuser
|
|
365
|
+
from sqlalchemy.orm import Session as DBSession
|
|
366
|
+
|
|
367
|
+
with DBSession(get_sync_engine()) as session:
|
|
368
|
+
user = session.query(User).filter(User.id == admin_user_id).first()
|
|
369
|
+
if user and user.is_superuser and user.is_active:
|
|
370
|
+
return True
|
|
371
|
+
|
|
372
|
+
# User no longer valid, clear session
|
|
373
|
+
request.session.clear()
|
|
374
|
+
return False
|
|
375
|
+
{%- endif %}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
CUSTOM_MODEL_CONFIGS: dict[type, dict[str, Any]] = {
|
|
379
|
+
User: {
|
|
380
|
+
"icon": "fa-solid fa-user",
|
|
381
|
+
"form_excluded_columns": [User.hashed_password, User.created_at, User.updated_at],
|
|
382
|
+
},
|
|
383
|
+
{%- if cookiecutter.enable_session_management %}
|
|
384
|
+
Session: {
|
|
385
|
+
"icon": "fa-solid fa-key",
|
|
386
|
+
"form_excluded_columns": [Session.refresh_token_hash],
|
|
387
|
+
"can_create": False, # Sessions are created via login
|
|
388
|
+
},
|
|
389
|
+
{%- endif %}
|
|
390
|
+
{%- if cookiecutter.enable_conversation_persistence %}
|
|
391
|
+
ToolCall: {
|
|
392
|
+
"icon": "fa-solid fa-wrench",
|
|
393
|
+
"can_create": False, # Tool calls are created by the agent
|
|
394
|
+
},
|
|
395
|
+
{%- endif %}
|
|
396
|
+
{%- if cookiecutter.enable_webhooks %}
|
|
397
|
+
Webhook: {
|
|
398
|
+
"icon": "fa-solid fa-link",
|
|
399
|
+
"form_excluded_columns": [Webhook.secret],
|
|
400
|
+
},
|
|
401
|
+
WebhookDelivery: {
|
|
402
|
+
"icon": "fa-solid fa-paper-plane",
|
|
403
|
+
"can_create": False, # Deliveries are created by webhook dispatch
|
|
404
|
+
"can_edit": False,
|
|
405
|
+
},
|
|
406
|
+
{%- endif %}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def setup_admin(app) -> Admin:
|
|
411
|
+
"""Setup SQLAdmin for the FastAPI app with automatic model discovery.
|
|
412
|
+
|
|
413
|
+
Automatically discovers all SQLAlchemy models from the Base registry
|
|
414
|
+
and creates admin views for them with sensible defaults.
|
|
415
|
+
|
|
416
|
+
Custom configurations can be provided in CUSTOM_MODEL_CONFIGS to override
|
|
417
|
+
default behavior for specific models.
|
|
418
|
+
"""
|
|
419
|
+
sync_engine = get_sync_engine()
|
|
420
|
+
|
|
421
|
+
{%- if cookiecutter.admin_require_auth %}
|
|
422
|
+
authentication_backend = AdminAuth(secret_key=settings.SECRET_KEY)
|
|
423
|
+
admin = Admin(
|
|
424
|
+
app,
|
|
425
|
+
sync_engine,
|
|
426
|
+
title="{{ cookiecutter.project_name }} Admin",
|
|
427
|
+
authentication_backend=authentication_backend,
|
|
428
|
+
)
|
|
429
|
+
{%- else %}
|
|
430
|
+
admin = Admin(
|
|
431
|
+
app,
|
|
432
|
+
sync_engine,
|
|
433
|
+
title="{{ cookiecutter.project_name }} Admin",
|
|
434
|
+
)
|
|
435
|
+
{%- endif %}
|
|
436
|
+
|
|
437
|
+
# Auto-register all models from Base with custom configs
|
|
438
|
+
register_models_auto(
|
|
439
|
+
admin,
|
|
440
|
+
Base,
|
|
441
|
+
custom_configs=CUSTOM_MODEL_CONFIGS,
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
return admin
|
|
445
|
+
{%- else %}
|
|
446
|
+
"""Admin panel - not configured."""
|
|
447
|
+
{%- endif %}
|