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,395 @@
|
|
|
1
|
+
{%- if cookiecutter.use_jwt %}
|
|
2
|
+
"""Authentication routes."""
|
|
3
|
+
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
{%- if cookiecutter.use_postgresql and not cookiecutter.enable_session_management %}
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
{%- endif %}
|
|
8
|
+
|
|
9
|
+
from fastapi import APIRouter, Depends{% if cookiecutter.enable_session_management %}, Request{% endif %}, status
|
|
10
|
+
from fastapi.security import OAuth2PasswordRequestForm
|
|
11
|
+
|
|
12
|
+
from app.api.deps import CurrentUser{% if cookiecutter.enable_session_management %}, SessionSvc{% endif %}, UserSvc
|
|
13
|
+
from app.core.exceptions import AuthenticationError
|
|
14
|
+
from app.core.security import create_access_token, create_refresh_token, verify_token
|
|
15
|
+
from app.schemas.token import RefreshTokenRequest, Token
|
|
16
|
+
from app.schemas.user import UserCreate, UserRead
|
|
17
|
+
|
|
18
|
+
router = APIRouter()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
{%- if cookiecutter.use_postgresql %}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@router.post("/login", response_model=Token)
|
|
25
|
+
async def login(
|
|
26
|
+
{%- if cookiecutter.enable_session_management %}
|
|
27
|
+
request: Request,
|
|
28
|
+
{%- endif %}
|
|
29
|
+
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
|
|
30
|
+
user_service: UserSvc,
|
|
31
|
+
{%- if cookiecutter.enable_session_management %}
|
|
32
|
+
session_service: SessionSvc,
|
|
33
|
+
{%- endif %}
|
|
34
|
+
):
|
|
35
|
+
"""OAuth2 compatible token login.
|
|
36
|
+
|
|
37
|
+
Returns access token and refresh token.
|
|
38
|
+
Raises domain exceptions handled by exception handlers.
|
|
39
|
+
"""
|
|
40
|
+
user = await user_service.authenticate(form_data.username, form_data.password)
|
|
41
|
+
access_token = create_access_token(subject=str(user.id))
|
|
42
|
+
refresh_token = create_refresh_token(subject=str(user.id))
|
|
43
|
+
{%- if cookiecutter.enable_session_management %}
|
|
44
|
+
|
|
45
|
+
# Create session to track this login
|
|
46
|
+
await session_service.create_session(
|
|
47
|
+
user_id=user.id,
|
|
48
|
+
refresh_token=refresh_token,
|
|
49
|
+
ip_address=request.client.host if request.client else None,
|
|
50
|
+
user_agent=request.headers.get("User-Agent"),
|
|
51
|
+
)
|
|
52
|
+
{%- endif %}
|
|
53
|
+
return Token(access_token=access_token, refresh_token=refresh_token)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@router.post("/register", response_model=UserRead, status_code=status.HTTP_201_CREATED)
|
|
57
|
+
async def register(
|
|
58
|
+
user_in: UserCreate,
|
|
59
|
+
user_service: UserSvc,
|
|
60
|
+
):
|
|
61
|
+
"""Register a new user.
|
|
62
|
+
|
|
63
|
+
Raises AlreadyExistsError if email is already registered.
|
|
64
|
+
"""
|
|
65
|
+
user = await user_service.register(user_in)
|
|
66
|
+
return user
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@router.post("/refresh", response_model=Token)
|
|
70
|
+
async def refresh_token(
|
|
71
|
+
{%- if cookiecutter.enable_session_management %}
|
|
72
|
+
request: Request,
|
|
73
|
+
{%- endif %}
|
|
74
|
+
body: RefreshTokenRequest,
|
|
75
|
+
user_service: UserSvc,
|
|
76
|
+
{%- if cookiecutter.enable_session_management %}
|
|
77
|
+
session_service: SessionSvc,
|
|
78
|
+
{%- endif %}
|
|
79
|
+
):
|
|
80
|
+
"""Get new access token using refresh token.
|
|
81
|
+
|
|
82
|
+
Raises AuthenticationError if refresh token is invalid or expired.
|
|
83
|
+
"""
|
|
84
|
+
{%- if cookiecutter.enable_session_management %}
|
|
85
|
+
|
|
86
|
+
# Validate refresh token against stored session
|
|
87
|
+
session = await session_service.validate_refresh_token(body.refresh_token)
|
|
88
|
+
if not session:
|
|
89
|
+
raise AuthenticationError(message="Invalid or expired refresh token")
|
|
90
|
+
|
|
91
|
+
user = await user_service.get_by_id(session.user_id)
|
|
92
|
+
{%- else %}
|
|
93
|
+
|
|
94
|
+
payload = verify_token(body.refresh_token)
|
|
95
|
+
if payload is None:
|
|
96
|
+
raise AuthenticationError(message="Invalid or expired refresh token")
|
|
97
|
+
|
|
98
|
+
if payload.get("type") != "refresh":
|
|
99
|
+
raise AuthenticationError(message="Invalid token type")
|
|
100
|
+
|
|
101
|
+
user_id = payload.get("sub")
|
|
102
|
+
if user_id is None:
|
|
103
|
+
raise AuthenticationError(message="Invalid token payload")
|
|
104
|
+
|
|
105
|
+
# Verify user still exists and is active
|
|
106
|
+
user = await user_service.get_by_id(UUID(user_id))
|
|
107
|
+
{%- endif %}
|
|
108
|
+
if not user.is_active:
|
|
109
|
+
raise AuthenticationError(message="User account is disabled")
|
|
110
|
+
|
|
111
|
+
access_token = create_access_token(subject=str(user.id))
|
|
112
|
+
new_refresh_token = create_refresh_token(subject=str(user.id))
|
|
113
|
+
{%- if cookiecutter.enable_session_management %}
|
|
114
|
+
|
|
115
|
+
# Invalidate old session and create new one
|
|
116
|
+
await session_service.logout_by_refresh_token(body.refresh_token)
|
|
117
|
+
await session_service.create_session(
|
|
118
|
+
user_id=user.id,
|
|
119
|
+
refresh_token=new_refresh_token,
|
|
120
|
+
ip_address=request.client.host if request.client else None,
|
|
121
|
+
user_agent=request.headers.get("User-Agent"),
|
|
122
|
+
)
|
|
123
|
+
{%- endif %}
|
|
124
|
+
return Token(access_token=access_token, refresh_token=new_refresh_token)
|
|
125
|
+
|
|
126
|
+
{%- if cookiecutter.enable_session_management %}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
|
130
|
+
async def logout(
|
|
131
|
+
body: RefreshTokenRequest,
|
|
132
|
+
session_service: SessionSvc,
|
|
133
|
+
):
|
|
134
|
+
"""Logout and invalidate the current session.
|
|
135
|
+
|
|
136
|
+
Invalidates the refresh token, preventing further token refresh.
|
|
137
|
+
"""
|
|
138
|
+
await session_service.logout_by_refresh_token(body.refresh_token)
|
|
139
|
+
{%- endif %}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@router.get("/me", response_model=UserRead)
|
|
143
|
+
async def get_current_user_info(current_user: CurrentUser):
|
|
144
|
+
"""Get current authenticated user information."""
|
|
145
|
+
return current_user
|
|
146
|
+
{%- elif cookiecutter.use_mongodb %}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@router.post("/login", response_model=Token)
|
|
150
|
+
async def login(
|
|
151
|
+
{%- if cookiecutter.enable_session_management %}
|
|
152
|
+
request: Request,
|
|
153
|
+
{%- endif %}
|
|
154
|
+
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
|
|
155
|
+
user_service: UserSvc,
|
|
156
|
+
{%- if cookiecutter.enable_session_management %}
|
|
157
|
+
session_service: SessionSvc,
|
|
158
|
+
{%- endif %}
|
|
159
|
+
):
|
|
160
|
+
"""OAuth2 compatible token login.
|
|
161
|
+
|
|
162
|
+
Returns access token and refresh token.
|
|
163
|
+
Raises domain exceptions handled by exception handlers.
|
|
164
|
+
"""
|
|
165
|
+
user = await user_service.authenticate(form_data.username, form_data.password)
|
|
166
|
+
access_token = create_access_token(subject=str(user.id))
|
|
167
|
+
refresh_token = create_refresh_token(subject=str(user.id))
|
|
168
|
+
{%- if cookiecutter.enable_session_management %}
|
|
169
|
+
|
|
170
|
+
# Create session to track this login
|
|
171
|
+
await session_service.create_session(
|
|
172
|
+
user_id=str(user.id),
|
|
173
|
+
refresh_token=refresh_token,
|
|
174
|
+
ip_address=request.client.host if request.client else None,
|
|
175
|
+
user_agent=request.headers.get("User-Agent"),
|
|
176
|
+
)
|
|
177
|
+
{%- endif %}
|
|
178
|
+
return Token(access_token=access_token, refresh_token=refresh_token)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@router.post("/register", response_model=UserRead, status_code=status.HTTP_201_CREATED)
|
|
182
|
+
async def register(
|
|
183
|
+
user_in: UserCreate,
|
|
184
|
+
user_service: UserSvc,
|
|
185
|
+
):
|
|
186
|
+
"""Register a new user.
|
|
187
|
+
|
|
188
|
+
Raises AlreadyExistsError if email is already registered.
|
|
189
|
+
"""
|
|
190
|
+
user = await user_service.register(user_in)
|
|
191
|
+
return user
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@router.post("/refresh", response_model=Token)
|
|
195
|
+
async def refresh_token(
|
|
196
|
+
{%- if cookiecutter.enable_session_management %}
|
|
197
|
+
request: Request,
|
|
198
|
+
{%- endif %}
|
|
199
|
+
body: RefreshTokenRequest,
|
|
200
|
+
user_service: UserSvc,
|
|
201
|
+
{%- if cookiecutter.enable_session_management %}
|
|
202
|
+
session_service: SessionSvc,
|
|
203
|
+
{%- endif %}
|
|
204
|
+
):
|
|
205
|
+
"""Get new access token using refresh token.
|
|
206
|
+
|
|
207
|
+
Raises AuthenticationError if refresh token is invalid or expired.
|
|
208
|
+
"""
|
|
209
|
+
{%- if cookiecutter.enable_session_management %}
|
|
210
|
+
# Validate refresh token against stored session
|
|
211
|
+
session = await session_service.validate_refresh_token(body.refresh_token)
|
|
212
|
+
if not session:
|
|
213
|
+
raise AuthenticationError(message="Invalid or expired refresh token")
|
|
214
|
+
|
|
215
|
+
user = await user_service.get_by_id(session.user_id)
|
|
216
|
+
{%- else %}
|
|
217
|
+
payload = verify_token(body.refresh_token)
|
|
218
|
+
if payload is None:
|
|
219
|
+
raise AuthenticationError(message="Invalid or expired refresh token")
|
|
220
|
+
|
|
221
|
+
if payload.get("type") != "refresh":
|
|
222
|
+
raise AuthenticationError(message="Invalid token type")
|
|
223
|
+
|
|
224
|
+
user_id = payload.get("sub")
|
|
225
|
+
if user_id is None:
|
|
226
|
+
raise AuthenticationError(message="Invalid token payload")
|
|
227
|
+
|
|
228
|
+
# Verify user still exists and is active
|
|
229
|
+
user = await user_service.get_by_id(user_id)
|
|
230
|
+
{%- endif %}
|
|
231
|
+
if not user.is_active:
|
|
232
|
+
raise AuthenticationError(message="User account is disabled")
|
|
233
|
+
|
|
234
|
+
access_token = create_access_token(subject=str(user.id))
|
|
235
|
+
new_refresh_token = create_refresh_token(subject=str(user.id))
|
|
236
|
+
{%- if cookiecutter.enable_session_management %}
|
|
237
|
+
|
|
238
|
+
# Invalidate old session and create new one
|
|
239
|
+
await session_service.logout_by_refresh_token(body.refresh_token)
|
|
240
|
+
await session_service.create_session(
|
|
241
|
+
user_id=str(user.id),
|
|
242
|
+
refresh_token=new_refresh_token,
|
|
243
|
+
ip_address=request.client.host if request.client else None,
|
|
244
|
+
user_agent=request.headers.get("User-Agent"),
|
|
245
|
+
)
|
|
246
|
+
{%- endif %}
|
|
247
|
+
return Token(access_token=access_token, refresh_token=new_refresh_token)
|
|
248
|
+
|
|
249
|
+
{%- if cookiecutter.enable_session_management %}
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
|
253
|
+
async def logout(
|
|
254
|
+
body: RefreshTokenRequest,
|
|
255
|
+
session_service: SessionSvc,
|
|
256
|
+
):
|
|
257
|
+
"""Logout and invalidate the current session.
|
|
258
|
+
|
|
259
|
+
Invalidates the refresh token, preventing further token refresh.
|
|
260
|
+
"""
|
|
261
|
+
await session_service.logout_by_refresh_token(body.refresh_token)
|
|
262
|
+
{%- endif %}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@router.get("/me", response_model=UserRead)
|
|
266
|
+
async def get_current_user_info(current_user: CurrentUser):
|
|
267
|
+
"""Get current authenticated user information."""
|
|
268
|
+
return current_user
|
|
269
|
+
{%- elif cookiecutter.use_sqlite %}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@router.post("/login", response_model=Token)
|
|
273
|
+
def login(
|
|
274
|
+
{%- if cookiecutter.enable_session_management %}
|
|
275
|
+
request: Request,
|
|
276
|
+
{%- endif %}
|
|
277
|
+
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
|
|
278
|
+
user_service: UserSvc,
|
|
279
|
+
{%- if cookiecutter.enable_session_management %}
|
|
280
|
+
session_service: SessionSvc,
|
|
281
|
+
{%- endif %}
|
|
282
|
+
):
|
|
283
|
+
"""OAuth2 compatible token login.
|
|
284
|
+
|
|
285
|
+
Returns access token and refresh token.
|
|
286
|
+
Raises domain exceptions handled by exception handlers.
|
|
287
|
+
"""
|
|
288
|
+
user = user_service.authenticate(form_data.username, form_data.password)
|
|
289
|
+
access_token = create_access_token(subject=user.id)
|
|
290
|
+
refresh_token = create_refresh_token(subject=user.id)
|
|
291
|
+
{%- if cookiecutter.enable_session_management %}
|
|
292
|
+
|
|
293
|
+
# Create session to track this login
|
|
294
|
+
session_service.create_session(
|
|
295
|
+
user_id=user.id,
|
|
296
|
+
refresh_token=refresh_token,
|
|
297
|
+
ip_address=request.client.host if request.client else None,
|
|
298
|
+
user_agent=request.headers.get("User-Agent"),
|
|
299
|
+
)
|
|
300
|
+
{%- endif %}
|
|
301
|
+
return Token(access_token=access_token, refresh_token=refresh_token)
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
@router.post("/register", response_model=UserRead, status_code=status.HTTP_201_CREATED)
|
|
305
|
+
def register(
|
|
306
|
+
user_in: UserCreate,
|
|
307
|
+
user_service: UserSvc,
|
|
308
|
+
):
|
|
309
|
+
"""Register a new user.
|
|
310
|
+
|
|
311
|
+
Raises AlreadyExistsError if email is already registered.
|
|
312
|
+
"""
|
|
313
|
+
user = user_service.register(user_in)
|
|
314
|
+
return user
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@router.post("/refresh", response_model=Token)
|
|
318
|
+
def refresh_token(
|
|
319
|
+
{%- if cookiecutter.enable_session_management %}
|
|
320
|
+
request: Request,
|
|
321
|
+
{%- endif %}
|
|
322
|
+
body: RefreshTokenRequest,
|
|
323
|
+
user_service: UserSvc,
|
|
324
|
+
{%- if cookiecutter.enable_session_management %}
|
|
325
|
+
session_service: SessionSvc,
|
|
326
|
+
{%- endif %}
|
|
327
|
+
):
|
|
328
|
+
"""Get new access token using refresh token.
|
|
329
|
+
|
|
330
|
+
Raises AuthenticationError if refresh token is invalid or expired.
|
|
331
|
+
"""
|
|
332
|
+
{%- if cookiecutter.enable_session_management %}
|
|
333
|
+
# Validate refresh token against stored session
|
|
334
|
+
session = session_service.validate_refresh_token(body.refresh_token)
|
|
335
|
+
if not session:
|
|
336
|
+
raise AuthenticationError(message="Invalid or expired refresh token")
|
|
337
|
+
|
|
338
|
+
user = user_service.get_by_id(session.user_id)
|
|
339
|
+
{%- else %}
|
|
340
|
+
payload = verify_token(body.refresh_token)
|
|
341
|
+
if payload is None:
|
|
342
|
+
raise AuthenticationError(message="Invalid or expired refresh token")
|
|
343
|
+
|
|
344
|
+
if payload.get("type") != "refresh":
|
|
345
|
+
raise AuthenticationError(message="Invalid token type")
|
|
346
|
+
|
|
347
|
+
user_id = payload.get("sub")
|
|
348
|
+
if user_id is None:
|
|
349
|
+
raise AuthenticationError(message="Invalid token payload")
|
|
350
|
+
|
|
351
|
+
# Verify user still exists and is active
|
|
352
|
+
user = user_service.get_by_id(user_id)
|
|
353
|
+
{%- endif %}
|
|
354
|
+
if not user.is_active:
|
|
355
|
+
raise AuthenticationError(message="User account is disabled")
|
|
356
|
+
|
|
357
|
+
access_token = create_access_token(subject=user.id)
|
|
358
|
+
new_refresh_token = create_refresh_token(subject=user.id)
|
|
359
|
+
{%- if cookiecutter.enable_session_management %}
|
|
360
|
+
|
|
361
|
+
# Invalidate old session and create new one
|
|
362
|
+
session_service.logout_by_refresh_token(body.refresh_token)
|
|
363
|
+
session_service.create_session(
|
|
364
|
+
user_id=user.id,
|
|
365
|
+
refresh_token=new_refresh_token,
|
|
366
|
+
ip_address=request.client.host if request.client else None,
|
|
367
|
+
user_agent=request.headers.get("User-Agent"),
|
|
368
|
+
)
|
|
369
|
+
{%- endif %}
|
|
370
|
+
return Token(access_token=access_token, refresh_token=new_refresh_token)
|
|
371
|
+
|
|
372
|
+
{%- if cookiecutter.enable_session_management %}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
|
376
|
+
def logout(
|
|
377
|
+
body: RefreshTokenRequest,
|
|
378
|
+
session_service: SessionSvc,
|
|
379
|
+
):
|
|
380
|
+
"""Logout and invalidate the current session.
|
|
381
|
+
|
|
382
|
+
Invalidates the refresh token, preventing further token refresh.
|
|
383
|
+
"""
|
|
384
|
+
session_service.logout_by_refresh_token(body.refresh_token)
|
|
385
|
+
{%- endif %}
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@router.get("/me", response_model=UserRead)
|
|
389
|
+
def get_current_user_info(current_user: CurrentUser):
|
|
390
|
+
"""Get current authenticated user information."""
|
|
391
|
+
return current_user
|
|
392
|
+
{%- endif %}
|
|
393
|
+
{%- else %}
|
|
394
|
+
"""Auth routes - not configured."""
|
|
395
|
+
{%- endif %}
|