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,218 @@
|
|
|
1
|
+
{%- if cookiecutter.use_jwt and cookiecutter.use_postgresql %}
|
|
2
|
+
"""User database model."""
|
|
3
|
+
|
|
4
|
+
import uuid
|
|
5
|
+
from enum import Enum
|
|
6
|
+
{%- if cookiecutter.enable_session_management %}
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
{%- endif %}
|
|
9
|
+
|
|
10
|
+
from sqlalchemy import Boolean, String
|
|
11
|
+
from sqlalchemy.dialects.postgresql import UUID
|
|
12
|
+
from sqlalchemy.orm import Mapped, mapped_column{% if cookiecutter.enable_session_management %}, relationship{% endif %}
|
|
13
|
+
|
|
14
|
+
from app.db.base import Base, TimestampMixin
|
|
15
|
+
|
|
16
|
+
{%- if cookiecutter.enable_session_management %}
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from app.db.models.session import Session
|
|
19
|
+
{%- endif %}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class UserRole(str, Enum):
|
|
23
|
+
"""User role enumeration.
|
|
24
|
+
|
|
25
|
+
Roles hierarchy (higher includes lower permissions):
|
|
26
|
+
- ADMIN: Full system access, can manage users and settings
|
|
27
|
+
- USER: Standard user access
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
ADMIN = "admin"
|
|
31
|
+
USER = "user"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class User(Base, TimestampMixin):
|
|
35
|
+
"""User model."""
|
|
36
|
+
|
|
37
|
+
__tablename__ = "users"
|
|
38
|
+
|
|
39
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
40
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
41
|
+
)
|
|
42
|
+
email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
|
|
43
|
+
hashed_password: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
44
|
+
full_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
45
|
+
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
46
|
+
is_superuser: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
47
|
+
role: Mapped[str] = mapped_column(String(50), default=UserRole.USER.value, nullable=False)
|
|
48
|
+
{%- if cookiecutter.enable_oauth %}
|
|
49
|
+
oauth_provider: Mapped[str | None] = mapped_column(String(50), nullable=True, index=True)
|
|
50
|
+
oauth_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
|
|
51
|
+
{%- endif %}
|
|
52
|
+
|
|
53
|
+
{%- if cookiecutter.enable_session_management %}
|
|
54
|
+
|
|
55
|
+
# Relationship to sessions
|
|
56
|
+
sessions: Mapped[list["Session"]] = relationship(
|
|
57
|
+
"Session", back_populates="user", cascade="all, delete-orphan"
|
|
58
|
+
)
|
|
59
|
+
{%- endif %}
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def user_role(self) -> UserRole:
|
|
63
|
+
"""Get role as enum."""
|
|
64
|
+
return UserRole(self.role)
|
|
65
|
+
|
|
66
|
+
def has_role(self, required_role: UserRole) -> bool:
|
|
67
|
+
"""Check if user has the required role or higher.
|
|
68
|
+
|
|
69
|
+
Admin role has access to everything.
|
|
70
|
+
"""
|
|
71
|
+
if self.role == UserRole.ADMIN.value:
|
|
72
|
+
return True
|
|
73
|
+
return self.role == required_role.value
|
|
74
|
+
|
|
75
|
+
def __repr__(self) -> str:
|
|
76
|
+
return f"<User(id={self.id}, email={self.email}, role={self.role})>"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
{%- elif cookiecutter.use_jwt and cookiecutter.use_sqlite %}
|
|
80
|
+
"""User database model."""
|
|
81
|
+
|
|
82
|
+
import uuid
|
|
83
|
+
from enum import Enum
|
|
84
|
+
{%- if cookiecutter.enable_session_management %}
|
|
85
|
+
from typing import TYPE_CHECKING
|
|
86
|
+
{%- endif %}
|
|
87
|
+
|
|
88
|
+
from sqlalchemy import Boolean, String
|
|
89
|
+
from sqlalchemy.orm import Mapped, mapped_column{% if cookiecutter.enable_session_management %}, relationship{% endif %}
|
|
90
|
+
|
|
91
|
+
from app.db.base import Base, TimestampMixin
|
|
92
|
+
|
|
93
|
+
{%- if cookiecutter.enable_session_management %}
|
|
94
|
+
if TYPE_CHECKING:
|
|
95
|
+
from app.db.models.session import Session
|
|
96
|
+
{%- endif %}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class UserRole(str, Enum):
|
|
100
|
+
"""User role enumeration.
|
|
101
|
+
|
|
102
|
+
Roles hierarchy (higher includes lower permissions):
|
|
103
|
+
- ADMIN: Full system access, can manage users and settings
|
|
104
|
+
- USER: Standard user access
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
ADMIN = "admin"
|
|
108
|
+
USER = "user"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class User(Base, TimestampMixin):
|
|
112
|
+
"""User model."""
|
|
113
|
+
|
|
114
|
+
__tablename__ = "users"
|
|
115
|
+
|
|
116
|
+
id: Mapped[str] = mapped_column(
|
|
117
|
+
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
118
|
+
)
|
|
119
|
+
email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
|
|
120
|
+
hashed_password: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
121
|
+
full_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
122
|
+
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
123
|
+
is_superuser: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
124
|
+
role: Mapped[str] = mapped_column(String(50), default=UserRole.USER.value, nullable=False)
|
|
125
|
+
{%- if cookiecutter.enable_oauth %}
|
|
126
|
+
oauth_provider: Mapped[str | None] = mapped_column(String(50), nullable=True, index=True)
|
|
127
|
+
oauth_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
|
|
128
|
+
{%- endif %}
|
|
129
|
+
|
|
130
|
+
{%- if cookiecutter.enable_session_management %}
|
|
131
|
+
|
|
132
|
+
# Relationship to sessions
|
|
133
|
+
sessions: Mapped[list["Session"]] = relationship(
|
|
134
|
+
"Session", back_populates="user", cascade="all, delete-orphan"
|
|
135
|
+
)
|
|
136
|
+
{%- endif %}
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def user_role(self) -> UserRole:
|
|
140
|
+
"""Get role as enum."""
|
|
141
|
+
return UserRole(self.role)
|
|
142
|
+
|
|
143
|
+
def has_role(self, required_role: UserRole) -> bool:
|
|
144
|
+
"""Check if user has the required role or higher.
|
|
145
|
+
|
|
146
|
+
Admin role has access to everything.
|
|
147
|
+
"""
|
|
148
|
+
if self.role == UserRole.ADMIN.value:
|
|
149
|
+
return True
|
|
150
|
+
return self.role == required_role.value
|
|
151
|
+
|
|
152
|
+
def __repr__(self) -> str:
|
|
153
|
+
return f"<User(id={self.id}, email={self.email}, role={self.role})>"
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
{%- elif cookiecutter.use_jwt and cookiecutter.use_mongodb %}
|
|
157
|
+
"""User document model for MongoDB."""
|
|
158
|
+
|
|
159
|
+
from datetime import UTC, datetime
|
|
160
|
+
from enum import Enum
|
|
161
|
+
from typing import Optional
|
|
162
|
+
|
|
163
|
+
from beanie import Document
|
|
164
|
+
from pydantic import EmailStr, Field
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class UserRole(str, Enum):
|
|
168
|
+
"""User role enumeration.
|
|
169
|
+
|
|
170
|
+
Roles hierarchy (higher includes lower permissions):
|
|
171
|
+
- ADMIN: Full system access, can manage users and settings
|
|
172
|
+
- USER: Standard user access
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
ADMIN = "admin"
|
|
176
|
+
USER = "user"
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class User(Document):
|
|
180
|
+
"""User document model."""
|
|
181
|
+
|
|
182
|
+
email: EmailStr
|
|
183
|
+
hashed_password: Optional[str] = None
|
|
184
|
+
full_name: Optional[str] = None
|
|
185
|
+
is_active: bool = True
|
|
186
|
+
is_superuser: bool = False
|
|
187
|
+
role: str = UserRole.USER.value
|
|
188
|
+
{%- if cookiecutter.enable_oauth %}
|
|
189
|
+
oauth_provider: Optional[str] = None
|
|
190
|
+
oauth_id: Optional[str] = None
|
|
191
|
+
{%- endif %}
|
|
192
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
193
|
+
updated_at: Optional[datetime] = None
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def user_role(self) -> UserRole:
|
|
197
|
+
"""Get role as enum."""
|
|
198
|
+
return UserRole(self.role)
|
|
199
|
+
|
|
200
|
+
def has_role(self, required_role: UserRole) -> bool:
|
|
201
|
+
"""Check if user has the required role or higher.
|
|
202
|
+
|
|
203
|
+
Admin role has access to everything.
|
|
204
|
+
"""
|
|
205
|
+
if self.role == UserRole.ADMIN.value:
|
|
206
|
+
return True
|
|
207
|
+
return self.role == required_role.value
|
|
208
|
+
|
|
209
|
+
class Settings:
|
|
210
|
+
name = "users"
|
|
211
|
+
indexes = [
|
|
212
|
+
"email",
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
{%- else %}
|
|
217
|
+
"""User model - not configured."""
|
|
218
|
+
{%- endif %}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
|
|
2
|
+
{%- if cookiecutter.use_postgresql %}
|
|
3
|
+
"""Webhook database models (PostgreSQL async)."""
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
|
|
10
|
+
from sqlalchemy.dialects.postgresql import ARRAY, UUID
|
|
11
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
12
|
+
|
|
13
|
+
from app.db.base import Base, TimestampMixin
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class WebhookEventType(str, Enum):
|
|
17
|
+
"""Webhook event types."""
|
|
18
|
+
|
|
19
|
+
# User events
|
|
20
|
+
USER_CREATED = "user.created"
|
|
21
|
+
USER_UPDATED = "user.updated"
|
|
22
|
+
USER_DELETED = "user.deleted"
|
|
23
|
+
|
|
24
|
+
# Custom events (extend as needed)
|
|
25
|
+
ITEM_CREATED = "item.created"
|
|
26
|
+
ITEM_UPDATED = "item.updated"
|
|
27
|
+
ITEM_DELETED = "item.deleted"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Webhook(Base, TimestampMixin):
|
|
31
|
+
"""Webhook subscription model."""
|
|
32
|
+
|
|
33
|
+
__tablename__ = "webhooks"
|
|
34
|
+
|
|
35
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
36
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
37
|
+
)
|
|
38
|
+
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
39
|
+
url: Mapped[str] = mapped_column(String(2048), nullable=False)
|
|
40
|
+
secret: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
41
|
+
events: Mapped[list[str]] = mapped_column(ARRAY(String), nullable=False)
|
|
42
|
+
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
43
|
+
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
44
|
+
|
|
45
|
+
# Optional: Associate webhook with a user
|
|
46
|
+
{%- if cookiecutter.use_jwt %}
|
|
47
|
+
user_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
48
|
+
UUID(as_uuid=True), ForeignKey("users.id"), nullable=True
|
|
49
|
+
)
|
|
50
|
+
{%- endif %}
|
|
51
|
+
|
|
52
|
+
# Relationship to delivery logs
|
|
53
|
+
deliveries: Mapped[list["WebhookDelivery"]] = relationship(
|
|
54
|
+
"WebhookDelivery", back_populates="webhook", cascade="all, delete-orphan"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class WebhookDelivery(Base):
|
|
59
|
+
"""Webhook delivery log model."""
|
|
60
|
+
|
|
61
|
+
__tablename__ = "webhook_deliveries"
|
|
62
|
+
|
|
63
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
64
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
65
|
+
)
|
|
66
|
+
webhook_id: Mapped[uuid.UUID] = mapped_column(
|
|
67
|
+
UUID(as_uuid=True), ForeignKey("webhooks.id"), nullable=False
|
|
68
|
+
)
|
|
69
|
+
event_type: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
70
|
+
payload: Mapped[str] = mapped_column(Text, nullable=False)
|
|
71
|
+
response_status: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
72
|
+
response_body: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
73
|
+
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
74
|
+
attempt_count: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
|
|
75
|
+
success: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
76
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
77
|
+
DateTime, default=datetime.utcnow, nullable=False
|
|
78
|
+
)
|
|
79
|
+
delivered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
|
80
|
+
|
|
81
|
+
# Relationship
|
|
82
|
+
webhook: Mapped["Webhook"] = relationship("Webhook", back_populates="deliveries")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
{%- elif cookiecutter.use_sqlite %}
|
|
86
|
+
"""Webhook database models (SQLite sync)."""
|
|
87
|
+
|
|
88
|
+
import uuid
|
|
89
|
+
from datetime import datetime
|
|
90
|
+
from enum import Enum
|
|
91
|
+
|
|
92
|
+
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
|
|
93
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
94
|
+
|
|
95
|
+
from app.db.base import Base, TimestampMixin
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class WebhookEventType(str, Enum):
|
|
99
|
+
"""Webhook event types."""
|
|
100
|
+
|
|
101
|
+
# User events
|
|
102
|
+
USER_CREATED = "user.created"
|
|
103
|
+
USER_UPDATED = "user.updated"
|
|
104
|
+
USER_DELETED = "user.deleted"
|
|
105
|
+
|
|
106
|
+
# Custom events (extend as needed)
|
|
107
|
+
ITEM_CREATED = "item.created"
|
|
108
|
+
ITEM_UPDATED = "item.updated"
|
|
109
|
+
ITEM_DELETED = "item.deleted"
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class Webhook(Base, TimestampMixin):
|
|
113
|
+
"""Webhook subscription model."""
|
|
114
|
+
|
|
115
|
+
__tablename__ = "webhooks"
|
|
116
|
+
|
|
117
|
+
id: Mapped[str] = mapped_column(
|
|
118
|
+
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
119
|
+
)
|
|
120
|
+
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
121
|
+
url: Mapped[str] = mapped_column(String(2048), nullable=False)
|
|
122
|
+
secret: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
123
|
+
# Store events as comma-separated string for SQLite
|
|
124
|
+
events_json: Mapped[str] = mapped_column(Text, nullable=False)
|
|
125
|
+
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
126
|
+
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
127
|
+
|
|
128
|
+
{%- if cookiecutter.use_jwt %}
|
|
129
|
+
user_id: Mapped[str | None] = mapped_column(
|
|
130
|
+
String(36), ForeignKey("users.id"), nullable=True
|
|
131
|
+
)
|
|
132
|
+
{%- endif %}
|
|
133
|
+
|
|
134
|
+
deliveries: Mapped[list["WebhookDelivery"]] = relationship(
|
|
135
|
+
"WebhookDelivery", back_populates="webhook", cascade="all, delete-orphan"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def events(self) -> list[str]:
|
|
140
|
+
"""Parse events from JSON string."""
|
|
141
|
+
import json
|
|
142
|
+
return json.loads(self.events_json) if self.events_json else []
|
|
143
|
+
|
|
144
|
+
@events.setter
|
|
145
|
+
def events(self, value: list[str]) -> None:
|
|
146
|
+
"""Store events as JSON string."""
|
|
147
|
+
import json
|
|
148
|
+
self.events_json = json.dumps(value)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class WebhookDelivery(Base):
|
|
152
|
+
"""Webhook delivery log model."""
|
|
153
|
+
|
|
154
|
+
__tablename__ = "webhook_deliveries"
|
|
155
|
+
|
|
156
|
+
id: Mapped[str] = mapped_column(
|
|
157
|
+
String(36), primary_key=True, default=lambda: str(uuid.uuid4())
|
|
158
|
+
)
|
|
159
|
+
webhook_id: Mapped[str] = mapped_column(
|
|
160
|
+
String(36), ForeignKey("webhooks.id"), nullable=False
|
|
161
|
+
)
|
|
162
|
+
event_type: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
163
|
+
payload: Mapped[str] = mapped_column(Text, nullable=False)
|
|
164
|
+
response_status: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
165
|
+
response_body: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
166
|
+
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
167
|
+
attempt_count: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
|
|
168
|
+
success: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
169
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
170
|
+
DateTime, default=datetime.utcnow, nullable=False
|
|
171
|
+
)
|
|
172
|
+
delivered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
|
173
|
+
|
|
174
|
+
webhook: Mapped["Webhook"] = relationship("Webhook", back_populates="deliveries")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
{%- elif cookiecutter.use_mongodb %}
|
|
178
|
+
"""Webhook document models (MongoDB)."""
|
|
179
|
+
|
|
180
|
+
from datetime import UTC, datetime
|
|
181
|
+
from enum import Enum
|
|
182
|
+
from typing import Optional
|
|
183
|
+
|
|
184
|
+
from beanie import Document
|
|
185
|
+
from pydantic import Field
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class WebhookEventType(str, Enum):
|
|
189
|
+
"""Webhook event types."""
|
|
190
|
+
|
|
191
|
+
# User events
|
|
192
|
+
USER_CREATED = "user.created"
|
|
193
|
+
USER_UPDATED = "user.updated"
|
|
194
|
+
USER_DELETED = "user.deleted"
|
|
195
|
+
|
|
196
|
+
# Custom events
|
|
197
|
+
ITEM_CREATED = "item.created"
|
|
198
|
+
ITEM_UPDATED = "item.updated"
|
|
199
|
+
ITEM_DELETED = "item.deleted"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class WebhookDelivery(Document):
|
|
203
|
+
"""Webhook delivery log document."""
|
|
204
|
+
|
|
205
|
+
webhook_id: str
|
|
206
|
+
event_type: str
|
|
207
|
+
payload: str
|
|
208
|
+
response_status: Optional[int] = None
|
|
209
|
+
response_body: Optional[str] = None
|
|
210
|
+
error_message: Optional[str] = None
|
|
211
|
+
attempt_count: int = 1
|
|
212
|
+
success: bool = False
|
|
213
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
214
|
+
delivered_at: Optional[datetime] = None
|
|
215
|
+
|
|
216
|
+
class Settings:
|
|
217
|
+
name = "webhook_deliveries"
|
|
218
|
+
indexes = ["webhook_id", "event_type", "created_at"]
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class Webhook(Document):
|
|
222
|
+
"""Webhook subscription document."""
|
|
223
|
+
|
|
224
|
+
name: str
|
|
225
|
+
url: str
|
|
226
|
+
secret: str
|
|
227
|
+
events: list[str]
|
|
228
|
+
is_active: bool = True
|
|
229
|
+
description: Optional[str] = None
|
|
230
|
+
{%- if cookiecutter.use_jwt %}
|
|
231
|
+
user_id: Optional[str] = None
|
|
232
|
+
{%- endif %}
|
|
233
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
234
|
+
updated_at: Optional[datetime] = None
|
|
235
|
+
|
|
236
|
+
class Settings:
|
|
237
|
+
name = "webhooks"
|
|
238
|
+
indexes = ["events", "is_active"]
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
{%- endif %}
|
|
242
|
+
{%- else %}
|
|
243
|
+
"""Webhook models - not configured."""
|
|
244
|
+
{%- endif %}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{%- if cookiecutter.use_postgresql %}
|
|
2
|
+
"""Async PostgreSQL database session."""
|
|
3
|
+
|
|
4
|
+
from collections.abc import AsyncGenerator
|
|
5
|
+
from contextlib import asynccontextmanager
|
|
6
|
+
|
|
7
|
+
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
|
8
|
+
|
|
9
|
+
from app.core.config import settings
|
|
10
|
+
|
|
11
|
+
engine = create_async_engine(
|
|
12
|
+
settings.DATABASE_URL,
|
|
13
|
+
echo=settings.DEBUG,
|
|
14
|
+
pool_size=settings.DB_POOL_SIZE,
|
|
15
|
+
max_overflow=settings.DB_MAX_OVERFLOW,
|
|
16
|
+
pool_timeout=settings.DB_POOL_TIMEOUT,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
async_session_maker = async_sessionmaker(
|
|
20
|
+
engine,
|
|
21
|
+
class_=AsyncSession,
|
|
22
|
+
expire_on_commit=False,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
|
|
27
|
+
"""Get async database session for FastAPI dependency injection.
|
|
28
|
+
|
|
29
|
+
Use this with FastAPI Depends().
|
|
30
|
+
"""
|
|
31
|
+
async with async_session_maker() as session:
|
|
32
|
+
try:
|
|
33
|
+
yield session
|
|
34
|
+
await session.commit()
|
|
35
|
+
except Exception:
|
|
36
|
+
await session.rollback()
|
|
37
|
+
raise
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@asynccontextmanager
|
|
41
|
+
async def get_db_context() -> AsyncGenerator[AsyncSession, None]:
|
|
42
|
+
"""Get async database session as context manager.
|
|
43
|
+
|
|
44
|
+
Use this with 'async with' for manual session management (e.g., WebSockets).
|
|
45
|
+
"""
|
|
46
|
+
async with async_session_maker() as session:
|
|
47
|
+
try:
|
|
48
|
+
yield session
|
|
49
|
+
await session.commit()
|
|
50
|
+
except Exception:
|
|
51
|
+
await session.rollback()
|
|
52
|
+
raise
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def close_db() -> None:
|
|
56
|
+
"""Close database connections."""
|
|
57
|
+
await engine.dispose()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
{%- elif cookiecutter.use_mongodb %}
|
|
61
|
+
"""Async MongoDB database session."""
|
|
62
|
+
|
|
63
|
+
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
|
|
64
|
+
|
|
65
|
+
from app.core.config import settings
|
|
66
|
+
|
|
67
|
+
client: AsyncIOMotorClient | None = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def get_db_session() -> AsyncIOMotorDatabase:
|
|
71
|
+
"""Get MongoDB database instance."""
|
|
72
|
+
global client
|
|
73
|
+
if client is None:
|
|
74
|
+
client = AsyncIOMotorClient(settings.MONGO_URL)
|
|
75
|
+
return client[settings.MONGO_DB]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def close_db() -> None:
|
|
79
|
+
"""Close MongoDB connection."""
|
|
80
|
+
global client
|
|
81
|
+
if client is not None:
|
|
82
|
+
client.close()
|
|
83
|
+
client = None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
{%- elif cookiecutter.use_sqlite %}
|
|
87
|
+
"""Sync SQLite database session."""
|
|
88
|
+
|
|
89
|
+
from collections.abc import Generator
|
|
90
|
+
|
|
91
|
+
from sqlalchemy import create_engine
|
|
92
|
+
from sqlalchemy.orm import Session, sessionmaker
|
|
93
|
+
|
|
94
|
+
from app.core.config import settings
|
|
95
|
+
|
|
96
|
+
engine = create_engine(
|
|
97
|
+
settings.DATABASE_URL,
|
|
98
|
+
connect_args={"check_same_thread": False},
|
|
99
|
+
echo=settings.DEBUG,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_db_session() -> Generator[Session, None, None]:
|
|
106
|
+
"""Get sync database session."""
|
|
107
|
+
db = SessionLocal()
|
|
108
|
+
try:
|
|
109
|
+
yield db
|
|
110
|
+
db.commit()
|
|
111
|
+
except Exception:
|
|
112
|
+
db.rollback()
|
|
113
|
+
raise
|
|
114
|
+
finally:
|
|
115
|
+
db.close()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def close_db() -> None:
|
|
119
|
+
"""Close database connection."""
|
|
120
|
+
engine.dispose()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
{%- else %}
|
|
124
|
+
"""No database configured."""
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
async def get_db_session():
|
|
128
|
+
"""No-op when database is disabled."""
|
|
129
|
+
yield None
|
|
130
|
+
{%- endif %}
|