fastapi-fullstack 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_fullstack-0.1.7.dist-info/METADATA +739 -0
- fastapi_fullstack-0.1.7.dist-info/RECORD +241 -0
- fastapi_fullstack-0.1.7.dist-info/WHEEL +4 -0
- fastapi_fullstack-0.1.7.dist-info/entry_points.txt +2 -0
- fastapi_fullstack-0.1.7.dist-info/licenses/LICENSE +21 -0
- fastapi_gen/__init__.py +3 -0
- fastapi_gen/cli.py +442 -0
- fastapi_gen/config.py +356 -0
- fastapi_gen/generator.py +207 -0
- fastapi_gen/prompts.py +874 -0
- fastapi_gen/template/VARIABLES.md +276 -0
- fastapi_gen/template/cookiecutter.json +93 -0
- fastapi_gen/template/hooks/post_gen_project.py +355 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.env.prod.example +56 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +150 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore +109 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/AGENTS.md +55 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/CLAUDE.md +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/Makefile +315 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/README.md +768 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.dockerignore +60 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.env.example +155 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +32 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile +56 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +30 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini +48 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +3 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +447 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +226 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/langchain_assistant.py +226 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/prompts.py +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +13 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +541 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +98 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +87 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +902 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +395 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +498 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +227 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +275 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +205 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +168 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +333 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +477 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +46 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +221 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +14 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +88 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +117 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +75 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +28 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +266 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +267 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +153 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +122 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +101 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +23 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +58 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +271 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +102 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +41 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +319 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +96 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +126 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +218 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +244 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +130 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +334 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +154 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +838 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +222 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +318 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +322 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +358 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +50 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +57 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +192 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +52 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +42 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +89 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +38 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +850 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +246 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +333 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +432 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +561 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +38 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +25 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +106 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +29 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +92 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +438 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +180 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +1 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +242 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +151 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +113 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +310 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +253 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +151 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_admin.py +890 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +261 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +183 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +173 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +143 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +118 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +181 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +124 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +363 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +85 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +242 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +435 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.yml +241 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/adding_features.md +132 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/architecture.md +63 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/patterns.md +161 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/docs/testing.md +120 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.dockerignore +40 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.env.example +12 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.gitignore +45 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +19 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/README.md +648 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +134 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +207 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +14 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +84 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +84 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/package.json +69 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +101 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/layout.tsx +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/login/page.tsx +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/register/page.tsx +5 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/chat/page.tsx +48 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/dashboard/page.tsx +99 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/layout.tsx +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/profile/page.tsx +152 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/auth/callback/page.tsx +113 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/layout.tsx +46 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/page.tsx +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +58 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +24 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +39 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +50 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +54 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +26 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +41 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +108 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +73 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +323 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +22 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +29 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +2 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +120 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +153 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +234 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +72 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +328 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/copy-button.tsx +46 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +11 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/local-conversation-sidebar.tsx +295 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/markdown-content.tsx +167 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +79 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +18 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +79 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +32 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +3 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +97 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +65 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +2 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +82 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +53 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +105 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +35 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +75 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +56 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +82 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +13 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +21 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/sheet.tsx +109 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +7 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +97 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +203 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +181 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-local-chat.ts +165 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +105 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +37 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +90 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +39 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +78 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +31 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +72 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +64 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-sidebar-store.ts +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +65 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +76 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +9 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/local-chat-store.ts +255 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/sidebar-store.ts +17 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +44 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +27 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +52 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +83 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +49 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +10 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +28 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +36 -0
- fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +56 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""FastAPI application entry point."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import AsyncGenerator
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
{%- if cookiecutter.enable_redis %}
|
|
6
|
+
from typing import TypedDict
|
|
7
|
+
{%- endif %}
|
|
8
|
+
|
|
9
|
+
from fastapi import FastAPI
|
|
10
|
+
{%- if cookiecutter.enable_orjson %}
|
|
11
|
+
from fastapi.responses import ORJSONResponse
|
|
12
|
+
{%- endif %}
|
|
13
|
+
{%- if cookiecutter.enable_pagination %}
|
|
14
|
+
from fastapi_pagination import add_pagination
|
|
15
|
+
{%- endif %}
|
|
16
|
+
|
|
17
|
+
from app.api.exception_handlers import register_exception_handlers
|
|
18
|
+
from app.api.router import api_router
|
|
19
|
+
from app.core.config import settings
|
|
20
|
+
from app.core.logfire_setup import instrument_app, setup_logfire
|
|
21
|
+
from app.core.middleware import RequestIDMiddleware
|
|
22
|
+
|
|
23
|
+
{%- if cookiecutter.enable_redis %}
|
|
24
|
+
from app.clients.redis import RedisClient
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LifespanState(TypedDict):
|
|
28
|
+
"""Lifespan state - resources available via request.state."""
|
|
29
|
+
|
|
30
|
+
redis: RedisClient
|
|
31
|
+
{%- endif %}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@asynccontextmanager
|
|
35
|
+
async def lifespan(app: FastAPI) -> AsyncGenerator[{% if cookiecutter.enable_redis %}LifespanState{% else %}None{% endif %}, None]:
|
|
36
|
+
"""Application lifespan - startup and shutdown events.
|
|
37
|
+
|
|
38
|
+
Resources yielded here are available via request.state in route handlers.
|
|
39
|
+
See: https://asgi.readthedocs.io/en/latest/specs/lifespan.html#lifespan-state
|
|
40
|
+
"""
|
|
41
|
+
# === Startup ===
|
|
42
|
+
setup_logfire()
|
|
43
|
+
|
|
44
|
+
{%- if cookiecutter.use_postgresql and cookiecutter.enable_logfire and cookiecutter.logfire_database %}
|
|
45
|
+
from app.core.logfire_setup import instrument_asyncpg
|
|
46
|
+
instrument_asyncpg()
|
|
47
|
+
{%- endif %}
|
|
48
|
+
|
|
49
|
+
{%- if cookiecutter.use_mongodb and cookiecutter.enable_logfire and cookiecutter.logfire_database %}
|
|
50
|
+
from app.core.logfire_setup import instrument_pymongo
|
|
51
|
+
instrument_pymongo()
|
|
52
|
+
{%- endif %}
|
|
53
|
+
|
|
54
|
+
{%- if cookiecutter.enable_redis and cookiecutter.enable_logfire and cookiecutter.logfire_redis %}
|
|
55
|
+
from app.core.logfire_setup import instrument_redis
|
|
56
|
+
instrument_redis()
|
|
57
|
+
{%- endif %}
|
|
58
|
+
|
|
59
|
+
{%- if cookiecutter.enable_logfire and cookiecutter.logfire_httpx %}
|
|
60
|
+
from app.core.logfire_setup import instrument_httpx
|
|
61
|
+
instrument_httpx()
|
|
62
|
+
{%- endif %}
|
|
63
|
+
|
|
64
|
+
{%- if cookiecutter.enable_logfire and cookiecutter.enable_ai_agent and cookiecutter.use_pydantic_ai %}
|
|
65
|
+
from app.core.logfire_setup import instrument_pydantic_ai
|
|
66
|
+
instrument_pydantic_ai()
|
|
67
|
+
{%- endif %}
|
|
68
|
+
|
|
69
|
+
{%- if cookiecutter.enable_redis %}
|
|
70
|
+
redis_client = RedisClient()
|
|
71
|
+
await redis_client.connect()
|
|
72
|
+
{%- endif %}
|
|
73
|
+
|
|
74
|
+
{%- if cookiecutter.enable_caching %}
|
|
75
|
+
from app.core.cache import setup_cache
|
|
76
|
+
setup_cache(redis_client)
|
|
77
|
+
{%- endif %}
|
|
78
|
+
|
|
79
|
+
{%- if cookiecutter.enable_redis %}
|
|
80
|
+
|
|
81
|
+
yield {"redis": redis_client}
|
|
82
|
+
|
|
83
|
+
# === Shutdown ===
|
|
84
|
+
await redis_client.close()
|
|
85
|
+
{%- else %}
|
|
86
|
+
|
|
87
|
+
yield
|
|
88
|
+
|
|
89
|
+
# === Shutdown ===
|
|
90
|
+
{%- endif %}
|
|
91
|
+
{%- if cookiecutter.use_postgresql %}
|
|
92
|
+
from app.db.session import close_db
|
|
93
|
+
await close_db()
|
|
94
|
+
{%- endif %}
|
|
95
|
+
|
|
96
|
+
{%- if cookiecutter.use_mongodb %}
|
|
97
|
+
from app.db.session import close_db
|
|
98
|
+
await close_db()
|
|
99
|
+
{%- endif %}
|
|
100
|
+
|
|
101
|
+
{%- if cookiecutter.use_sqlite %}
|
|
102
|
+
from app.db.session import close_db
|
|
103
|
+
close_db()
|
|
104
|
+
{%- endif %}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# Environments where API docs should be visible
|
|
108
|
+
SHOW_DOCS_ENVIRONMENTS = ("local", "staging", "development")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def create_app() -> FastAPI:
|
|
112
|
+
"""Create and configure the FastAPI application."""
|
|
113
|
+
# Only show docs in allowed environments (hide in production)
|
|
114
|
+
show_docs = settings.ENVIRONMENT in SHOW_DOCS_ENVIRONMENTS
|
|
115
|
+
openapi_url = f"{settings.API_V1_STR}/openapi.json" if show_docs else None
|
|
116
|
+
docs_url = "/docs" if show_docs else None
|
|
117
|
+
redoc_url = "/redoc" if show_docs else None
|
|
118
|
+
|
|
119
|
+
# OpenAPI tags for better documentation organization
|
|
120
|
+
openapi_tags = [
|
|
121
|
+
{
|
|
122
|
+
"name": "health",
|
|
123
|
+
"description": "Health check endpoints for monitoring and Kubernetes probes",
|
|
124
|
+
},
|
|
125
|
+
{%- if cookiecutter.use_jwt %}
|
|
126
|
+
{
|
|
127
|
+
"name": "auth",
|
|
128
|
+
"description": "Authentication endpoints - login, register, token refresh",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"name": "users",
|
|
132
|
+
"description": "User management endpoints",
|
|
133
|
+
},
|
|
134
|
+
{%- endif %}
|
|
135
|
+
{%- if cookiecutter.enable_oauth %}
|
|
136
|
+
{
|
|
137
|
+
"name": "oauth",
|
|
138
|
+
"description": "OAuth2 social login endpoints (Google, etc.)",
|
|
139
|
+
},
|
|
140
|
+
{%- endif %}
|
|
141
|
+
{%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
|
|
142
|
+
{
|
|
143
|
+
"name": "sessions",
|
|
144
|
+
"description": "Session management - view and manage active login sessions",
|
|
145
|
+
},
|
|
146
|
+
{%- endif %}
|
|
147
|
+
{%- if cookiecutter.include_example_crud %}
|
|
148
|
+
{
|
|
149
|
+
"name": "items",
|
|
150
|
+
"description": "Example CRUD endpoints demonstrating the API pattern",
|
|
151
|
+
},
|
|
152
|
+
{%- endif %}
|
|
153
|
+
{%- if cookiecutter.enable_conversation_persistence %}
|
|
154
|
+
{
|
|
155
|
+
"name": "conversations",
|
|
156
|
+
"description": "AI conversation persistence - manage chat history",
|
|
157
|
+
},
|
|
158
|
+
{%- endif %}
|
|
159
|
+
{%- if cookiecutter.enable_webhooks %}
|
|
160
|
+
{
|
|
161
|
+
"name": "webhooks",
|
|
162
|
+
"description": "Webhook management - subscribe to events and manage deliveries",
|
|
163
|
+
},
|
|
164
|
+
{%- endif %}
|
|
165
|
+
{%- if cookiecutter.enable_ai_agent %}
|
|
166
|
+
{
|
|
167
|
+
"name": "agent",
|
|
168
|
+
"description": "AI agent WebSocket endpoint for real-time chat",
|
|
169
|
+
},
|
|
170
|
+
{%- endif %}
|
|
171
|
+
{%- if cookiecutter.enable_websockets %}
|
|
172
|
+
{
|
|
173
|
+
"name": "websocket",
|
|
174
|
+
"description": "WebSocket endpoints for real-time communication",
|
|
175
|
+
},
|
|
176
|
+
{%- endif %}
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
app = FastAPI(
|
|
180
|
+
title=settings.PROJECT_NAME,
|
|
181
|
+
summary="FastAPI application with Logfire observability",
|
|
182
|
+
description="""
|
|
183
|
+
{{ cookiecutter.project_description }}
|
|
184
|
+
|
|
185
|
+
## Features
|
|
186
|
+
|
|
187
|
+
{%- if cookiecutter.use_jwt %}
|
|
188
|
+
- **Authentication**: JWT-based authentication with refresh tokens
|
|
189
|
+
{%- endif %}
|
|
190
|
+
{%- if cookiecutter.use_api_key %}
|
|
191
|
+
- **API Key**: Header-based API key authentication
|
|
192
|
+
{%- endif %}
|
|
193
|
+
{%- if cookiecutter.use_database %}
|
|
194
|
+
- **Database**: Async database operations
|
|
195
|
+
{%- endif %}
|
|
196
|
+
{%- if cookiecutter.enable_redis %}
|
|
197
|
+
- **Redis**: Caching and session storage
|
|
198
|
+
{%- endif %}
|
|
199
|
+
{%- if cookiecutter.enable_rate_limiting %}
|
|
200
|
+
- **Rate Limiting**: Request rate limiting per client
|
|
201
|
+
{%- endif %}
|
|
202
|
+
{%- if cookiecutter.enable_ai_agent and cookiecutter.use_pydantic_ai %}
|
|
203
|
+
- **AI Agent**: PydanticAI-powered conversational assistant
|
|
204
|
+
{%- endif %}
|
|
205
|
+
{%- if cookiecutter.enable_ai_agent and cookiecutter.use_langchain %}
|
|
206
|
+
- **AI Agent**: LangChain-powered conversational assistant
|
|
207
|
+
{%- endif %}
|
|
208
|
+
{%- if cookiecutter.enable_logfire %}
|
|
209
|
+
- **Observability**: Logfire integration for tracing and monitoring
|
|
210
|
+
{%- endif %}
|
|
211
|
+
|
|
212
|
+
## Documentation
|
|
213
|
+
|
|
214
|
+
- [Swagger UI](/docs) - Interactive API documentation
|
|
215
|
+
- [ReDoc](/redoc) - Alternative documentation view
|
|
216
|
+
""".strip(),
|
|
217
|
+
version="0.1.0",
|
|
218
|
+
openapi_url=openapi_url,
|
|
219
|
+
docs_url=docs_url,
|
|
220
|
+
redoc_url=redoc_url,
|
|
221
|
+
openapi_tags=openapi_tags,
|
|
222
|
+
contact={
|
|
223
|
+
"name": "{{ cookiecutter.author_name }}",
|
|
224
|
+
"email": "{{ cookiecutter.author_email }}",
|
|
225
|
+
},
|
|
226
|
+
license_info={
|
|
227
|
+
"name": "MIT",
|
|
228
|
+
"identifier": "MIT",
|
|
229
|
+
},
|
|
230
|
+
lifespan=lifespan,
|
|
231
|
+
{%- if cookiecutter.enable_orjson %}
|
|
232
|
+
default_response_class=ORJSONResponse,
|
|
233
|
+
{%- endif %}
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
# Logfire instrumentation
|
|
237
|
+
instrument_app(app)
|
|
238
|
+
|
|
239
|
+
# Request ID middleware (for request correlation/debugging)
|
|
240
|
+
app.add_middleware(RequestIDMiddleware)
|
|
241
|
+
|
|
242
|
+
# Exception handlers
|
|
243
|
+
register_exception_handlers(app)
|
|
244
|
+
|
|
245
|
+
{%- if cookiecutter.enable_cors %}
|
|
246
|
+
|
|
247
|
+
# CORS middleware
|
|
248
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
249
|
+
app.add_middleware(
|
|
250
|
+
CORSMiddleware,
|
|
251
|
+
allow_origins=settings.CORS_ORIGINS,
|
|
252
|
+
allow_credentials=settings.CORS_ALLOW_CREDENTIALS,
|
|
253
|
+
allow_methods=settings.CORS_ALLOW_METHODS,
|
|
254
|
+
allow_headers=settings.CORS_ALLOW_HEADERS,
|
|
255
|
+
)
|
|
256
|
+
{%- endif %}
|
|
257
|
+
|
|
258
|
+
{%- if cookiecutter.enable_sentry %}
|
|
259
|
+
|
|
260
|
+
# Sentry
|
|
261
|
+
if settings.SENTRY_DSN:
|
|
262
|
+
import sentry_sdk
|
|
263
|
+
sentry_sdk.init(dsn=settings.SENTRY_DSN, enable_tracing=True)
|
|
264
|
+
{%- endif %}
|
|
265
|
+
|
|
266
|
+
{%- if cookiecutter.enable_rate_limiting %}
|
|
267
|
+
|
|
268
|
+
# Rate limiting
|
|
269
|
+
# Note: slowapi requires app.state.limiter - this is a library requirement,
|
|
270
|
+
# not suitable for lifespan state pattern which is for request-scoped access
|
|
271
|
+
from app.core.rate_limit import limiter
|
|
272
|
+
from slowapi import _rate_limit_exceeded_handler
|
|
273
|
+
from slowapi.errors import RateLimitExceeded
|
|
274
|
+
app.state.limiter = limiter
|
|
275
|
+
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
|
276
|
+
{%- endif %}
|
|
277
|
+
|
|
278
|
+
{%- if (cookiecutter.enable_admin_panel and cookiecutter.use_postgresql and cookiecutter.admin_require_auth and not cookiecutter.admin_env_disabled) or cookiecutter.enable_oauth %}
|
|
279
|
+
|
|
280
|
+
# Session middleware (for admin authentication and/or OAuth)
|
|
281
|
+
from starlette.middleware.sessions import SessionMiddleware
|
|
282
|
+
app.add_middleware(SessionMiddleware, secret_key=settings.SECRET_KEY)
|
|
283
|
+
{%- endif %}
|
|
284
|
+
|
|
285
|
+
{%- if cookiecutter.enable_admin_panel and cookiecutter.use_postgresql %}
|
|
286
|
+
{%- if cookiecutter.admin_env_disabled %}
|
|
287
|
+
# Admin panel - disabled
|
|
288
|
+
{%- elif cookiecutter.admin_env_all %}
|
|
289
|
+
|
|
290
|
+
# Admin panel (all environments)
|
|
291
|
+
from app.admin import setup_admin
|
|
292
|
+
setup_admin(app)
|
|
293
|
+
{%- else %}
|
|
294
|
+
|
|
295
|
+
# Admin panel (environment restricted)
|
|
296
|
+
{%- if cookiecutter.admin_env_dev_only %}
|
|
297
|
+
ADMIN_ALLOWED_ENVIRONMENTS = ["development", "local"]
|
|
298
|
+
{%- elif cookiecutter.admin_env_dev_staging %}
|
|
299
|
+
ADMIN_ALLOWED_ENVIRONMENTS = ["development", "local", "staging"]
|
|
300
|
+
{%- endif %}
|
|
301
|
+
|
|
302
|
+
if settings.ENVIRONMENT in ADMIN_ALLOWED_ENVIRONMENTS:
|
|
303
|
+
from app.admin import setup_admin
|
|
304
|
+
setup_admin(app)
|
|
305
|
+
{%- endif %}
|
|
306
|
+
{%- endif %}
|
|
307
|
+
|
|
308
|
+
# API Version Deprecation (uncomment when deprecating old versions)
|
|
309
|
+
# Example: Mark v1 as deprecated when v2 is ready
|
|
310
|
+
# from app.api.versioning import VersionDeprecationMiddleware
|
|
311
|
+
# app.add_middleware(
|
|
312
|
+
# VersionDeprecationMiddleware,
|
|
313
|
+
# deprecated_versions={
|
|
314
|
+
# "v1": {
|
|
315
|
+
# "sunset": "2025-12-31",
|
|
316
|
+
# "link": "/docs/migration/v2",
|
|
317
|
+
# "message": "Please migrate to API v2",
|
|
318
|
+
# }
|
|
319
|
+
# },
|
|
320
|
+
# )
|
|
321
|
+
|
|
322
|
+
# Include API router
|
|
323
|
+
app.include_router(api_router, prefix=settings.API_V1_STR)
|
|
324
|
+
|
|
325
|
+
{%- if cookiecutter.enable_pagination %}
|
|
326
|
+
|
|
327
|
+
# Pagination
|
|
328
|
+
add_pagination(app)
|
|
329
|
+
{%- endif %}
|
|
330
|
+
|
|
331
|
+
return app
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
app = create_app()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Background processing pipelines.
|
|
2
|
+
|
|
3
|
+
This module contains ETL pipelines, data processing workflows,
|
|
4
|
+
and batch operations that run as background tasks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from app.pipelines.base import BasePipeline, PipelineResult
|
|
8
|
+
|
|
9
|
+
__all__ = ["BasePipeline", "PipelineResult"]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Base pipeline classes."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class PipelineResult:
|
|
9
|
+
"""Result of a pipeline execution.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
processed: Number of items successfully processed.
|
|
13
|
+
failed: Number of items that failed processing.
|
|
14
|
+
errors: List of error messages for failed items.
|
|
15
|
+
metadata: Additional metadata about the pipeline run.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
processed: int
|
|
19
|
+
failed: int = 0
|
|
20
|
+
errors: list[str] = field(default_factory=list)
|
|
21
|
+
metadata: dict = field(default_factory=dict)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def success_rate(self) -> float:
|
|
25
|
+
"""Calculate success rate as a percentage."""
|
|
26
|
+
total = self.processed + self.failed
|
|
27
|
+
if total == 0:
|
|
28
|
+
return 100.0
|
|
29
|
+
return (self.processed / total) * 100
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def has_errors(self) -> bool:
|
|
33
|
+
"""Check if any errors occurred."""
|
|
34
|
+
return self.failed > 0 or len(self.errors) > 0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class BasePipeline(ABC):
|
|
38
|
+
"""Base class for all pipelines.
|
|
39
|
+
|
|
40
|
+
Pipelines are used for background processing tasks like:
|
|
41
|
+
- ETL operations
|
|
42
|
+
- Batch data processing
|
|
43
|
+
- Embedding generation
|
|
44
|
+
- Data synchronization
|
|
45
|
+
|
|
46
|
+
Subclasses must implement the `run` method.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
async def run(self) -> PipelineResult:
|
|
51
|
+
"""Execute the pipeline.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
PipelineResult with processing statistics.
|
|
55
|
+
"""
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
async def validate(self) -> bool:
|
|
59
|
+
"""Validate pipeline configuration before running.
|
|
60
|
+
|
|
61
|
+
Override this method to add custom validation logic.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
True if validation passes, False otherwise.
|
|
65
|
+
"""
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
async def cleanup(self) -> None: # noqa: B027
|
|
69
|
+
"""Cleanup resources after pipeline execution.
|
|
70
|
+
|
|
71
|
+
Override this method to add custom cleanup logic.
|
|
72
|
+
Default implementation does nothing.
|
|
73
|
+
"""
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Repository layer for database operations."""
|
|
2
|
+
{%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite or cookiecutter.use_jwt or cookiecutter.include_example_crud or cookiecutter.enable_conversation_persistence or cookiecutter.enable_webhooks %}
|
|
3
|
+
# ruff: noqa: I001, RUF022 - Imports structured for Jinja2 template conditionals
|
|
4
|
+
{%- endif %}
|
|
5
|
+
{%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
|
|
6
|
+
|
|
7
|
+
from app.repositories.base import BaseRepository
|
|
8
|
+
{%- endif %}
|
|
9
|
+
{%- if cookiecutter.use_jwt %}
|
|
10
|
+
|
|
11
|
+
from app.repositories import user as user_repo
|
|
12
|
+
{%- endif %}
|
|
13
|
+
{%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
|
|
14
|
+
|
|
15
|
+
from app.repositories import session as session_repo
|
|
16
|
+
{%- endif %}
|
|
17
|
+
{%- if cookiecutter.include_example_crud and cookiecutter.use_database %}
|
|
18
|
+
|
|
19
|
+
from app.repositories import item as item_repo
|
|
20
|
+
{%- endif %}
|
|
21
|
+
{%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
|
|
22
|
+
|
|
23
|
+
from app.repositories import conversation as conversation_repo
|
|
24
|
+
{%- endif %}
|
|
25
|
+
{%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
|
|
26
|
+
|
|
27
|
+
from app.repositories import webhook as webhook_repo
|
|
28
|
+
{%- endif %}
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
{%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
|
|
32
|
+
"BaseRepository",
|
|
33
|
+
{%- endif %}
|
|
34
|
+
{%- if cookiecutter.use_jwt %}
|
|
35
|
+
"user_repo",
|
|
36
|
+
{%- endif %}
|
|
37
|
+
{%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
|
|
38
|
+
"session_repo",
|
|
39
|
+
{%- endif %}
|
|
40
|
+
{%- if cookiecutter.include_example_crud and cookiecutter.use_database %}
|
|
41
|
+
"item_repo",
|
|
42
|
+
{%- endif %}
|
|
43
|
+
{%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
|
|
44
|
+
"conversation_repo",
|
|
45
|
+
{%- endif %}
|
|
46
|
+
{%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
|
|
47
|
+
"webhook_repo",
|
|
48
|
+
{%- endif %}
|
|
49
|
+
]
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
|
|
2
|
+
"""Base repository with generic CRUD operations."""
|
|
3
|
+
|
|
4
|
+
from typing import Any, Generic, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from sqlalchemy import select
|
|
8
|
+
{%- if cookiecutter.use_postgresql %}
|
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
10
|
+
{%- else %}
|
|
11
|
+
from sqlalchemy.orm import Session
|
|
12
|
+
{%- endif %}
|
|
13
|
+
|
|
14
|
+
from app.db.base import Base
|
|
15
|
+
|
|
16
|
+
ModelType = TypeVar("ModelType", bound=Base)
|
|
17
|
+
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
|
|
18
|
+
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseRepository(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
|
22
|
+
"""Base class for repository operations.
|
|
23
|
+
|
|
24
|
+
Provides generic CRUD operations for SQLAlchemy models.
|
|
25
|
+
Subclasses should specify the model type via the model attribute.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, model: type[ModelType]):
|
|
29
|
+
self.model = model
|
|
30
|
+
|
|
31
|
+
{%- if cookiecutter.use_postgresql %}
|
|
32
|
+
|
|
33
|
+
async def get(self, db: AsyncSession, id: Any) -> ModelType | None:
|
|
34
|
+
"""Get a single record by ID."""
|
|
35
|
+
return await db.get(self.model, id)
|
|
36
|
+
|
|
37
|
+
async def get_multi(
|
|
38
|
+
self,
|
|
39
|
+
db: AsyncSession,
|
|
40
|
+
*,
|
|
41
|
+
skip: int = 0,
|
|
42
|
+
limit: int = 100,
|
|
43
|
+
) -> list[ModelType]:
|
|
44
|
+
"""Get multiple records with pagination."""
|
|
45
|
+
result = await db.execute(
|
|
46
|
+
select(self.model).offset(skip).limit(limit)
|
|
47
|
+
)
|
|
48
|
+
return list(result.scalars().all())
|
|
49
|
+
|
|
50
|
+
async def create(
|
|
51
|
+
self,
|
|
52
|
+
db: AsyncSession,
|
|
53
|
+
*,
|
|
54
|
+
obj_in: CreateSchemaType,
|
|
55
|
+
) -> ModelType:
|
|
56
|
+
"""Create a new record."""
|
|
57
|
+
obj_in_data = obj_in.model_dump()
|
|
58
|
+
db_obj = self.model(**obj_in_data)
|
|
59
|
+
db.add(db_obj)
|
|
60
|
+
await db.flush()
|
|
61
|
+
await db.refresh(db_obj)
|
|
62
|
+
return db_obj
|
|
63
|
+
|
|
64
|
+
async def update(
|
|
65
|
+
self,
|
|
66
|
+
db: AsyncSession,
|
|
67
|
+
*,
|
|
68
|
+
db_obj: ModelType,
|
|
69
|
+
obj_in: UpdateSchemaType | dict[str, Any],
|
|
70
|
+
) -> ModelType:
|
|
71
|
+
"""Update a record."""
|
|
72
|
+
update_data = obj_in if isinstance(obj_in, dict) else obj_in.model_dump(exclude_unset=True)
|
|
73
|
+
|
|
74
|
+
for field, value in update_data.items():
|
|
75
|
+
setattr(db_obj, field, value)
|
|
76
|
+
|
|
77
|
+
db.add(db_obj)
|
|
78
|
+
await db.flush()
|
|
79
|
+
await db.refresh(db_obj)
|
|
80
|
+
return db_obj
|
|
81
|
+
|
|
82
|
+
async def delete(self, db: AsyncSession, *, id: Any) -> ModelType | None:
|
|
83
|
+
"""Delete a record."""
|
|
84
|
+
obj = await self.get(db, id)
|
|
85
|
+
if obj:
|
|
86
|
+
await db.delete(obj)
|
|
87
|
+
await db.flush()
|
|
88
|
+
return obj
|
|
89
|
+
|
|
90
|
+
{%- else %}
|
|
91
|
+
|
|
92
|
+
def get(self, db: Session, id: Any) -> ModelType | None:
|
|
93
|
+
"""Get a single record by ID."""
|
|
94
|
+
return db.get(self.model, id)
|
|
95
|
+
|
|
96
|
+
def get_multi(
|
|
97
|
+
self,
|
|
98
|
+
db: Session,
|
|
99
|
+
*,
|
|
100
|
+
skip: int = 0,
|
|
101
|
+
limit: int = 100,
|
|
102
|
+
) -> list[ModelType]:
|
|
103
|
+
"""Get multiple records with pagination."""
|
|
104
|
+
result = db.execute(
|
|
105
|
+
select(self.model).offset(skip).limit(limit)
|
|
106
|
+
)
|
|
107
|
+
return list(result.scalars().all())
|
|
108
|
+
|
|
109
|
+
def create(
|
|
110
|
+
self,
|
|
111
|
+
db: Session,
|
|
112
|
+
*,
|
|
113
|
+
obj_in: CreateSchemaType,
|
|
114
|
+
) -> ModelType:
|
|
115
|
+
"""Create a new record."""
|
|
116
|
+
obj_in_data = obj_in.model_dump()
|
|
117
|
+
db_obj = self.model(**obj_in_data)
|
|
118
|
+
db.add(db_obj)
|
|
119
|
+
db.flush()
|
|
120
|
+
db.refresh(db_obj)
|
|
121
|
+
return db_obj
|
|
122
|
+
|
|
123
|
+
def update(
|
|
124
|
+
self,
|
|
125
|
+
db: Session,
|
|
126
|
+
*,
|
|
127
|
+
db_obj: ModelType,
|
|
128
|
+
obj_in: UpdateSchemaType | dict[str, Any],
|
|
129
|
+
) -> ModelType:
|
|
130
|
+
"""Update a record."""
|
|
131
|
+
if isinstance(obj_in, dict):
|
|
132
|
+
update_data = obj_in
|
|
133
|
+
else:
|
|
134
|
+
update_data = obj_in.model_dump(exclude_unset=True)
|
|
135
|
+
|
|
136
|
+
for field, value in update_data.items():
|
|
137
|
+
setattr(db_obj, field, value)
|
|
138
|
+
|
|
139
|
+
db.add(db_obj)
|
|
140
|
+
db.flush()
|
|
141
|
+
db.refresh(db_obj)
|
|
142
|
+
return db_obj
|
|
143
|
+
|
|
144
|
+
def delete(self, db: Session, *, id: Any) -> ModelType | None:
|
|
145
|
+
"""Delete a record."""
|
|
146
|
+
obj = self.get(db, id)
|
|
147
|
+
if obj:
|
|
148
|
+
db.delete(obj)
|
|
149
|
+
db.flush()
|
|
150
|
+
return obj
|
|
151
|
+
{%- endif %}
|
|
152
|
+
{%- else %}
|
|
153
|
+
"""Base repository - not using SQLAlchemy."""
|
|
154
|
+
{%- endif %}
|