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,432 @@
|
|
|
1
|
+
{%- if cookiecutter.use_jwt and cookiecutter.use_postgresql %}
|
|
2
|
+
"""User service (PostgreSQL async).
|
|
3
|
+
|
|
4
|
+
Contains business logic for user operations. Uses UserRepository for database access.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
10
|
+
|
|
11
|
+
from app.core.exceptions import AlreadyExistsError, AuthenticationError, NotFoundError
|
|
12
|
+
from app.core.security import get_password_hash, verify_password
|
|
13
|
+
from app.db.models.user import User
|
|
14
|
+
from app.repositories import user_repo
|
|
15
|
+
from app.schemas.user import UserCreate, UserUpdate
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class UserService:
|
|
19
|
+
"""Service for user-related business logic."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, db: AsyncSession):
|
|
22
|
+
self.db = db
|
|
23
|
+
|
|
24
|
+
async def get_by_id(self, user_id: UUID) -> User:
|
|
25
|
+
"""Get user by ID.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
NotFoundError: If user does not exist.
|
|
29
|
+
"""
|
|
30
|
+
user = await user_repo.get_by_id(self.db, user_id)
|
|
31
|
+
if not user:
|
|
32
|
+
raise NotFoundError(
|
|
33
|
+
message="User not found",
|
|
34
|
+
details={"user_id": user_id},
|
|
35
|
+
)
|
|
36
|
+
return user
|
|
37
|
+
|
|
38
|
+
async def get_by_email(self, email: str) -> User | None:
|
|
39
|
+
"""Get user by email. Returns None if not found."""
|
|
40
|
+
return await user_repo.get_by_email(self.db, email)
|
|
41
|
+
|
|
42
|
+
async def get_multi(
|
|
43
|
+
self,
|
|
44
|
+
*,
|
|
45
|
+
skip: int = 0,
|
|
46
|
+
limit: int = 100,
|
|
47
|
+
) -> list[User]:
|
|
48
|
+
"""Get multiple users with pagination."""
|
|
49
|
+
return await user_repo.get_multi(self.db, skip=skip, limit=limit)
|
|
50
|
+
|
|
51
|
+
async def register(self, user_in: UserCreate) -> User:
|
|
52
|
+
"""Register a new user.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
AlreadyExistsError: If email is already registered.
|
|
56
|
+
"""
|
|
57
|
+
existing = await user_repo.get_by_email(self.db, user_in.email)
|
|
58
|
+
if existing:
|
|
59
|
+
raise AlreadyExistsError(
|
|
60
|
+
message="Email already registered",
|
|
61
|
+
details={"email": user_in.email},
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
hashed_password = get_password_hash(user_in.password)
|
|
65
|
+
return await user_repo.create(
|
|
66
|
+
self.db,
|
|
67
|
+
email=user_in.email,
|
|
68
|
+
hashed_password=hashed_password,
|
|
69
|
+
full_name=user_in.full_name,
|
|
70
|
+
role=user_in.role.value,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
async def authenticate(self, email: str, password: str) -> User:
|
|
74
|
+
"""Authenticate user by email and password.
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
AuthenticationError: If credentials are invalid or user is inactive.
|
|
78
|
+
"""
|
|
79
|
+
user = await user_repo.get_by_email(self.db, email)
|
|
80
|
+
if not user or not verify_password(password, user.hashed_password):
|
|
81
|
+
raise AuthenticationError(message="Invalid email or password")
|
|
82
|
+
if not user.is_active:
|
|
83
|
+
raise AuthenticationError(message="User account is disabled")
|
|
84
|
+
return user
|
|
85
|
+
|
|
86
|
+
async def update(self, user_id: UUID, user_in: UserUpdate) -> User:
|
|
87
|
+
"""Update user.
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
NotFoundError: If user does not exist.
|
|
91
|
+
"""
|
|
92
|
+
user = await self.get_by_id(user_id)
|
|
93
|
+
|
|
94
|
+
update_data = user_in.model_dump(exclude_unset=True)
|
|
95
|
+
if "password" in update_data:
|
|
96
|
+
update_data["hashed_password"] = get_password_hash(update_data.pop("password"))
|
|
97
|
+
|
|
98
|
+
return await user_repo.update(self.db, db_user=user, update_data=update_data)
|
|
99
|
+
|
|
100
|
+
async def delete(self, user_id: UUID) -> User:
|
|
101
|
+
"""Delete user.
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
NotFoundError: If user does not exist.
|
|
105
|
+
"""
|
|
106
|
+
user = await user_repo.delete(self.db, user_id)
|
|
107
|
+
if not user:
|
|
108
|
+
raise NotFoundError(
|
|
109
|
+
message="User not found",
|
|
110
|
+
details={"user_id": str(user_id)},
|
|
111
|
+
)
|
|
112
|
+
return user
|
|
113
|
+
|
|
114
|
+
{%- if cookiecutter.enable_oauth %}
|
|
115
|
+
|
|
116
|
+
async def get_by_oauth(self, provider: str, oauth_id: str) -> User | None:
|
|
117
|
+
"""Get user by OAuth provider and ID."""
|
|
118
|
+
return await user_repo.get_by_oauth(self.db, provider, oauth_id)
|
|
119
|
+
|
|
120
|
+
async def link_oauth(self, user_id: UUID, provider: str, oauth_id: str) -> User:
|
|
121
|
+
"""Link OAuth account to existing user."""
|
|
122
|
+
user = await self.get_by_id(user_id)
|
|
123
|
+
return await user_repo.update(
|
|
124
|
+
self.db,
|
|
125
|
+
db_user=user,
|
|
126
|
+
update_data={"oauth_provider": provider, "oauth_id": oauth_id},
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
async def create_oauth_user(
|
|
130
|
+
self,
|
|
131
|
+
email: str,
|
|
132
|
+
full_name: str | None,
|
|
133
|
+
oauth_provider: str,
|
|
134
|
+
oauth_id: str,
|
|
135
|
+
) -> User:
|
|
136
|
+
"""Create a new user from OAuth data."""
|
|
137
|
+
return await user_repo.create(
|
|
138
|
+
self.db,
|
|
139
|
+
email=email,
|
|
140
|
+
hashed_password=None,
|
|
141
|
+
full_name=full_name,
|
|
142
|
+
oauth_provider=oauth_provider,
|
|
143
|
+
oauth_id=oauth_id,
|
|
144
|
+
)
|
|
145
|
+
{%- endif %}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
{%- elif cookiecutter.use_jwt and cookiecutter.use_sqlite %}
|
|
149
|
+
"""User service (SQLite sync).
|
|
150
|
+
|
|
151
|
+
Contains business logic for user operations. Uses UserRepository for database access.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
from sqlalchemy.orm import Session
|
|
155
|
+
|
|
156
|
+
from app.core.exceptions import AlreadyExistsError, AuthenticationError, NotFoundError
|
|
157
|
+
from app.core.security import get_password_hash, verify_password
|
|
158
|
+
from app.db.models.user import User
|
|
159
|
+
from app.repositories import user_repo
|
|
160
|
+
from app.schemas.user import UserCreate, UserUpdate
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class UserService:
|
|
164
|
+
"""Service for user-related business logic."""
|
|
165
|
+
|
|
166
|
+
def __init__(self, db: Session):
|
|
167
|
+
self.db = db
|
|
168
|
+
|
|
169
|
+
def get_by_id(self, user_id: str) -> User:
|
|
170
|
+
"""Get user by ID.
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
NotFoundError: If user does not exist.
|
|
174
|
+
"""
|
|
175
|
+
user = user_repo.get_by_id(self.db, user_id)
|
|
176
|
+
if not user:
|
|
177
|
+
raise NotFoundError(
|
|
178
|
+
message="User not found",
|
|
179
|
+
details={"user_id": user_id},
|
|
180
|
+
)
|
|
181
|
+
return user
|
|
182
|
+
|
|
183
|
+
def get_by_email(self, email: str) -> User | None:
|
|
184
|
+
"""Get user by email. Returns None if not found."""
|
|
185
|
+
return user_repo.get_by_email(self.db, email)
|
|
186
|
+
|
|
187
|
+
def get_multi(
|
|
188
|
+
self,
|
|
189
|
+
*,
|
|
190
|
+
skip: int = 0,
|
|
191
|
+
limit: int = 100,
|
|
192
|
+
) -> list[User]:
|
|
193
|
+
"""Get multiple users with pagination."""
|
|
194
|
+
return user_repo.get_multi(self.db, skip=skip, limit=limit)
|
|
195
|
+
|
|
196
|
+
def register(self, user_in: UserCreate) -> User:
|
|
197
|
+
"""Register a new user.
|
|
198
|
+
|
|
199
|
+
Raises:
|
|
200
|
+
AlreadyExistsError: If email is already registered.
|
|
201
|
+
"""
|
|
202
|
+
existing = user_repo.get_by_email(self.db, user_in.email)
|
|
203
|
+
if existing:
|
|
204
|
+
raise AlreadyExistsError(
|
|
205
|
+
message="Email already registered",
|
|
206
|
+
details={"email": user_in.email},
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
hashed_password = get_password_hash(user_in.password)
|
|
210
|
+
return user_repo.create(
|
|
211
|
+
self.db,
|
|
212
|
+
email=user_in.email,
|
|
213
|
+
hashed_password=hashed_password,
|
|
214
|
+
full_name=user_in.full_name,
|
|
215
|
+
role=user_in.role.value,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def authenticate(self, email: str, password: str) -> User:
|
|
219
|
+
"""Authenticate user by email and password.
|
|
220
|
+
|
|
221
|
+
Raises:
|
|
222
|
+
AuthenticationError: If credentials are invalid or user is inactive.
|
|
223
|
+
"""
|
|
224
|
+
user = user_repo.get_by_email(self.db, email)
|
|
225
|
+
if not user or not verify_password(password, user.hashed_password):
|
|
226
|
+
raise AuthenticationError(message="Invalid email or password")
|
|
227
|
+
if not user.is_active:
|
|
228
|
+
raise AuthenticationError(message="User account is disabled")
|
|
229
|
+
return user
|
|
230
|
+
|
|
231
|
+
def update(self, user_id: str, user_in: UserUpdate) -> User:
|
|
232
|
+
"""Update user.
|
|
233
|
+
|
|
234
|
+
Raises:
|
|
235
|
+
NotFoundError: If user does not exist.
|
|
236
|
+
"""
|
|
237
|
+
user = self.get_by_id(user_id)
|
|
238
|
+
|
|
239
|
+
update_data = user_in.model_dump(exclude_unset=True)
|
|
240
|
+
if "password" in update_data:
|
|
241
|
+
update_data["hashed_password"] = get_password_hash(update_data.pop("password"))
|
|
242
|
+
|
|
243
|
+
return user_repo.update(self.db, db_user=user, update_data=update_data)
|
|
244
|
+
|
|
245
|
+
def delete(self, user_id: str) -> User:
|
|
246
|
+
"""Delete user.
|
|
247
|
+
|
|
248
|
+
Raises:
|
|
249
|
+
NotFoundError: If user does not exist.
|
|
250
|
+
"""
|
|
251
|
+
user = user_repo.delete(self.db, user_id)
|
|
252
|
+
if not user:
|
|
253
|
+
raise NotFoundError(
|
|
254
|
+
message="User not found",
|
|
255
|
+
details={"user_id": user_id},
|
|
256
|
+
)
|
|
257
|
+
return user
|
|
258
|
+
|
|
259
|
+
{%- if cookiecutter.enable_oauth %}
|
|
260
|
+
|
|
261
|
+
def get_by_oauth(self, provider: str, oauth_id: str) -> User | None:
|
|
262
|
+
"""Get user by OAuth provider and ID."""
|
|
263
|
+
return user_repo.get_by_oauth(self.db, provider, oauth_id)
|
|
264
|
+
|
|
265
|
+
def link_oauth(self, user_id: str, provider: str, oauth_id: str) -> User:
|
|
266
|
+
"""Link OAuth account to existing user."""
|
|
267
|
+
user = self.get_by_id(user_id)
|
|
268
|
+
return user_repo.update(
|
|
269
|
+
self.db,
|
|
270
|
+
db_user=user,
|
|
271
|
+
update_data={"oauth_provider": provider, "oauth_id": oauth_id},
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
def create_oauth_user(
|
|
275
|
+
self,
|
|
276
|
+
email: str,
|
|
277
|
+
full_name: str | None,
|
|
278
|
+
oauth_provider: str,
|
|
279
|
+
oauth_id: str,
|
|
280
|
+
) -> User:
|
|
281
|
+
"""Create a new user from OAuth data."""
|
|
282
|
+
return user_repo.create(
|
|
283
|
+
self.db,
|
|
284
|
+
email=email,
|
|
285
|
+
hashed_password=None,
|
|
286
|
+
full_name=full_name,
|
|
287
|
+
oauth_provider=oauth_provider,
|
|
288
|
+
oauth_id=oauth_id,
|
|
289
|
+
)
|
|
290
|
+
{%- endif %}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
{%- elif cookiecutter.use_jwt and cookiecutter.use_mongodb %}
|
|
294
|
+
"""User service (MongoDB).
|
|
295
|
+
|
|
296
|
+
Contains business logic for user operations. Uses UserRepository for database access.
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
from app.core.exceptions import AlreadyExistsError, AuthenticationError, NotFoundError
|
|
300
|
+
from app.core.security import get_password_hash, verify_password
|
|
301
|
+
from app.db.models.user import User
|
|
302
|
+
from app.repositories import user_repo
|
|
303
|
+
from app.schemas.user import UserCreate, UserUpdate
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class UserService:
|
|
307
|
+
"""Service for user-related business logic."""
|
|
308
|
+
|
|
309
|
+
async def get_by_id(self, user_id: str) -> User:
|
|
310
|
+
"""Get user by ID.
|
|
311
|
+
|
|
312
|
+
Raises:
|
|
313
|
+
NotFoundError: If user does not exist.
|
|
314
|
+
"""
|
|
315
|
+
user = await user_repo.get_by_id(user_id)
|
|
316
|
+
if not user:
|
|
317
|
+
raise NotFoundError(
|
|
318
|
+
message="User not found",
|
|
319
|
+
details={"user_id": user_id},
|
|
320
|
+
)
|
|
321
|
+
return user
|
|
322
|
+
|
|
323
|
+
async def get_by_email(self, email: str) -> User | None:
|
|
324
|
+
"""Get user by email. Returns None if not found."""
|
|
325
|
+
return await user_repo.get_by_email(email)
|
|
326
|
+
|
|
327
|
+
async def get_multi(
|
|
328
|
+
self,
|
|
329
|
+
*,
|
|
330
|
+
skip: int = 0,
|
|
331
|
+
limit: int = 100,
|
|
332
|
+
) -> list[User]:
|
|
333
|
+
"""Get multiple users with pagination."""
|
|
334
|
+
return await user_repo.get_multi(skip=skip, limit=limit)
|
|
335
|
+
|
|
336
|
+
async def register(self, user_in: UserCreate) -> User:
|
|
337
|
+
"""Register a new user.
|
|
338
|
+
|
|
339
|
+
Raises:
|
|
340
|
+
AlreadyExistsError: If email is already registered.
|
|
341
|
+
"""
|
|
342
|
+
existing = await user_repo.get_by_email(user_in.email)
|
|
343
|
+
if existing:
|
|
344
|
+
raise AlreadyExistsError(
|
|
345
|
+
message="Email already registered",
|
|
346
|
+
details={"email": user_in.email},
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
hashed_password = get_password_hash(user_in.password)
|
|
350
|
+
return await user_repo.create(
|
|
351
|
+
email=user_in.email,
|
|
352
|
+
hashed_password=hashed_password,
|
|
353
|
+
full_name=user_in.full_name,
|
|
354
|
+
role=user_in.role.value,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
async def authenticate(self, email: str, password: str) -> User:
|
|
358
|
+
"""Authenticate user by email and password.
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
AuthenticationError: If credentials are invalid or user is inactive.
|
|
362
|
+
"""
|
|
363
|
+
user = await user_repo.get_by_email(email)
|
|
364
|
+
if not user or not verify_password(password, user.hashed_password):
|
|
365
|
+
raise AuthenticationError(message="Invalid email or password")
|
|
366
|
+
if not user.is_active:
|
|
367
|
+
raise AuthenticationError(message="User account is disabled")
|
|
368
|
+
return user
|
|
369
|
+
|
|
370
|
+
async def update(self, user_id: str, user_in: UserUpdate) -> User:
|
|
371
|
+
"""Update user.
|
|
372
|
+
|
|
373
|
+
Raises:
|
|
374
|
+
NotFoundError: If user does not exist.
|
|
375
|
+
"""
|
|
376
|
+
user = await self.get_by_id(user_id)
|
|
377
|
+
|
|
378
|
+
update_data = user_in.model_dump(exclude_unset=True)
|
|
379
|
+
if "password" in update_data:
|
|
380
|
+
update_data["hashed_password"] = get_password_hash(update_data.pop("password"))
|
|
381
|
+
|
|
382
|
+
return await user_repo.update(db_user=user, update_data=update_data)
|
|
383
|
+
|
|
384
|
+
async def delete(self, user_id: str) -> User:
|
|
385
|
+
"""Delete user.
|
|
386
|
+
|
|
387
|
+
Raises:
|
|
388
|
+
NotFoundError: If user does not exist.
|
|
389
|
+
"""
|
|
390
|
+
user = await user_repo.delete(user_id)
|
|
391
|
+
if not user:
|
|
392
|
+
raise NotFoundError(
|
|
393
|
+
message="User not found",
|
|
394
|
+
details={"user_id": user_id},
|
|
395
|
+
)
|
|
396
|
+
return user
|
|
397
|
+
|
|
398
|
+
{%- if cookiecutter.enable_oauth %}
|
|
399
|
+
|
|
400
|
+
async def get_by_oauth(self, provider: str, oauth_id: str) -> User | None:
|
|
401
|
+
"""Get user by OAuth provider and ID."""
|
|
402
|
+
return await user_repo.get_by_oauth(provider, oauth_id)
|
|
403
|
+
|
|
404
|
+
async def link_oauth(self, user_id: str, provider: str, oauth_id: str) -> User:
|
|
405
|
+
"""Link OAuth account to existing user."""
|
|
406
|
+
user = await self.get_by_id(user_id)
|
|
407
|
+
return await user_repo.update(
|
|
408
|
+
db_user=user,
|
|
409
|
+
update_data={"oauth_provider": provider, "oauth_id": oauth_id},
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
async def create_oauth_user(
|
|
413
|
+
self,
|
|
414
|
+
email: str,
|
|
415
|
+
full_name: str | None,
|
|
416
|
+
oauth_provider: str,
|
|
417
|
+
oauth_id: str,
|
|
418
|
+
) -> User:
|
|
419
|
+
"""Create a new user from OAuth data."""
|
|
420
|
+
return await user_repo.create(
|
|
421
|
+
email=email,
|
|
422
|
+
hashed_password=None,
|
|
423
|
+
full_name=full_name,
|
|
424
|
+
oauth_provider=oauth_provider,
|
|
425
|
+
oauth_id=oauth_id,
|
|
426
|
+
)
|
|
427
|
+
{%- endif %}
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
{%- else %}
|
|
431
|
+
"""User service - not configured."""
|
|
432
|
+
{%- endif %}
|