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,222 @@
|
|
|
1
|
+
{%- if cookiecutter.include_example_crud and cookiecutter.use_postgresql %}
|
|
2
|
+
"""Item repository (PostgreSQL async).
|
|
3
|
+
|
|
4
|
+
Contains database operations for Item entity. Business logic
|
|
5
|
+
should be handled by ItemService in app/services/item.py.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
|
|
10
|
+
from sqlalchemy import select
|
|
11
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
12
|
+
|
|
13
|
+
from app.db.models.item import Item
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def get_by_id(db: AsyncSession, item_id: UUID) -> Item | None:
|
|
17
|
+
"""Get item by ID."""
|
|
18
|
+
return await db.get(Item, item_id)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def get_multi(
|
|
22
|
+
db: AsyncSession,
|
|
23
|
+
*,
|
|
24
|
+
skip: int = 0,
|
|
25
|
+
limit: int = 100,
|
|
26
|
+
active_only: bool = False,
|
|
27
|
+
) -> list[Item]:
|
|
28
|
+
"""Get multiple items with pagination."""
|
|
29
|
+
query = select(Item)
|
|
30
|
+
if active_only:
|
|
31
|
+
query = query.where(Item.is_active == True) # noqa: E712
|
|
32
|
+
query = query.offset(skip).limit(limit)
|
|
33
|
+
result = await db.execute(query)
|
|
34
|
+
return list(result.scalars().all())
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
async def create(
|
|
38
|
+
db: AsyncSession,
|
|
39
|
+
*,
|
|
40
|
+
title: str,
|
|
41
|
+
description: str | None = None,
|
|
42
|
+
) -> Item:
|
|
43
|
+
"""Create a new item."""
|
|
44
|
+
item = Item(
|
|
45
|
+
title=title,
|
|
46
|
+
description=description,
|
|
47
|
+
)
|
|
48
|
+
db.add(item)
|
|
49
|
+
await db.flush()
|
|
50
|
+
await db.refresh(item)
|
|
51
|
+
return item
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def update(
|
|
55
|
+
db: AsyncSession,
|
|
56
|
+
*,
|
|
57
|
+
db_item: Item,
|
|
58
|
+
update_data: dict,
|
|
59
|
+
) -> Item:
|
|
60
|
+
"""Update an item."""
|
|
61
|
+
for field, value in update_data.items():
|
|
62
|
+
setattr(db_item, field, value)
|
|
63
|
+
|
|
64
|
+
db.add(db_item)
|
|
65
|
+
await db.flush()
|
|
66
|
+
await db.refresh(db_item)
|
|
67
|
+
return db_item
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def delete(db: AsyncSession, item_id: UUID) -> Item | None:
|
|
71
|
+
"""Delete an item."""
|
|
72
|
+
item = await get_by_id(db, item_id)
|
|
73
|
+
if item:
|
|
74
|
+
await db.delete(item)
|
|
75
|
+
await db.flush()
|
|
76
|
+
return item
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
{%- elif cookiecutter.include_example_crud and cookiecutter.use_sqlite %}
|
|
80
|
+
"""Item repository (SQLite sync).
|
|
81
|
+
|
|
82
|
+
Contains database operations for Item entity. Business logic
|
|
83
|
+
should be handled by ItemService in app/services/item.py.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
from sqlalchemy import select
|
|
87
|
+
from sqlalchemy.orm import Session
|
|
88
|
+
|
|
89
|
+
from app.db.models.item import Item
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_by_id(db: Session, item_id: str) -> Item | None:
|
|
93
|
+
"""Get item by ID."""
|
|
94
|
+
return db.get(Item, item_id)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_multi(
|
|
98
|
+
db: Session,
|
|
99
|
+
*,
|
|
100
|
+
skip: int = 0,
|
|
101
|
+
limit: int = 100,
|
|
102
|
+
active_only: bool = False,
|
|
103
|
+
) -> list[Item]:
|
|
104
|
+
"""Get multiple items with pagination."""
|
|
105
|
+
query = select(Item)
|
|
106
|
+
if active_only:
|
|
107
|
+
query = query.where(Item.is_active == True) # noqa: E712
|
|
108
|
+
query = query.offset(skip).limit(limit)
|
|
109
|
+
result = db.execute(query)
|
|
110
|
+
return list(result.scalars().all())
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def create(
|
|
114
|
+
db: Session,
|
|
115
|
+
*,
|
|
116
|
+
title: str,
|
|
117
|
+
description: str | None = None,
|
|
118
|
+
) -> Item:
|
|
119
|
+
"""Create a new item."""
|
|
120
|
+
item = Item(
|
|
121
|
+
title=title,
|
|
122
|
+
description=description,
|
|
123
|
+
)
|
|
124
|
+
db.add(item)
|
|
125
|
+
db.flush()
|
|
126
|
+
db.refresh(item)
|
|
127
|
+
return item
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def update(
|
|
131
|
+
db: Session,
|
|
132
|
+
*,
|
|
133
|
+
db_item: Item,
|
|
134
|
+
update_data: dict,
|
|
135
|
+
) -> Item:
|
|
136
|
+
"""Update an item."""
|
|
137
|
+
for field, value in update_data.items():
|
|
138
|
+
setattr(db_item, field, value)
|
|
139
|
+
|
|
140
|
+
db.add(db_item)
|
|
141
|
+
db.flush()
|
|
142
|
+
db.refresh(db_item)
|
|
143
|
+
return db_item
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def delete(db: Session, item_id: str) -> Item | None:
|
|
147
|
+
"""Delete an item."""
|
|
148
|
+
item = get_by_id(db, item_id)
|
|
149
|
+
if item:
|
|
150
|
+
db.delete(item)
|
|
151
|
+
db.flush()
|
|
152
|
+
return item
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
{%- elif cookiecutter.include_example_crud and cookiecutter.use_mongodb %}
|
|
156
|
+
"""Item repository (MongoDB).
|
|
157
|
+
|
|
158
|
+
Contains database operations for Item entity. Business logic
|
|
159
|
+
should be handled by ItemService in app/services/item.py.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
from datetime import UTC, datetime
|
|
163
|
+
|
|
164
|
+
from app.db.models.item import Item
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def get_by_id(item_id: str) -> Item | None:
|
|
168
|
+
"""Get item by ID."""
|
|
169
|
+
return await Item.get(item_id)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
async def get_multi(
|
|
173
|
+
*,
|
|
174
|
+
skip: int = 0,
|
|
175
|
+
limit: int = 100,
|
|
176
|
+
active_only: bool = False,
|
|
177
|
+
) -> list[Item]:
|
|
178
|
+
"""Get multiple items with pagination."""
|
|
179
|
+
query = Item.find_all()
|
|
180
|
+
if active_only:
|
|
181
|
+
query = Item.find(Item.is_active == True) # noqa: E712
|
|
182
|
+
return await query.skip(skip).limit(limit).to_list()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async def create(
|
|
186
|
+
*,
|
|
187
|
+
title: str,
|
|
188
|
+
description: str | None = None,
|
|
189
|
+
) -> Item:
|
|
190
|
+
"""Create a new item."""
|
|
191
|
+
item = Item(
|
|
192
|
+
title=title,
|
|
193
|
+
description=description,
|
|
194
|
+
)
|
|
195
|
+
await item.insert()
|
|
196
|
+
return item
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
async def update(
|
|
200
|
+
*,
|
|
201
|
+
db_item: Item,
|
|
202
|
+
update_data: dict,
|
|
203
|
+
) -> Item:
|
|
204
|
+
"""Update an item."""
|
|
205
|
+
for field, value in update_data.items():
|
|
206
|
+
setattr(db_item, field, value)
|
|
207
|
+
db_item.updated_at = datetime.now(UTC)
|
|
208
|
+
await db_item.save()
|
|
209
|
+
return db_item
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
async def delete(item_id: str) -> Item | None:
|
|
213
|
+
"""Delete an item."""
|
|
214
|
+
item = await get_by_id(item_id)
|
|
215
|
+
if item:
|
|
216
|
+
await item.delete()
|
|
217
|
+
return item
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
{%- else %}
|
|
221
|
+
"""Item repository - not configured."""
|
|
222
|
+
{%- endif %}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
|
|
2
|
+
{%- if cookiecutter.use_postgresql %}
|
|
3
|
+
"""Session repository (PostgreSQL async)."""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import select, update
|
|
9
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
10
|
+
|
|
11
|
+
from app.db.models.session import Session
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def get_by_id(db: AsyncSession, session_id: UUID) -> Session | None:
|
|
15
|
+
"""Get session by ID."""
|
|
16
|
+
return await db.get(Session, session_id)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def get_by_refresh_token_hash(db: AsyncSession, token_hash: str) -> Session | None:
|
|
20
|
+
"""Get session by refresh token hash."""
|
|
21
|
+
result = await db.execute(
|
|
22
|
+
select(Session).where(
|
|
23
|
+
Session.refresh_token_hash == token_hash,
|
|
24
|
+
Session.is_active.is_(True),
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
return result.scalar_one_or_none()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def get_user_sessions(
|
|
31
|
+
db: AsyncSession,
|
|
32
|
+
user_id: UUID,
|
|
33
|
+
*,
|
|
34
|
+
active_only: bool = True,
|
|
35
|
+
) -> list[Session]:
|
|
36
|
+
"""Get all sessions for a user."""
|
|
37
|
+
query = select(Session).where(Session.user_id == user_id)
|
|
38
|
+
if active_only:
|
|
39
|
+
query = query.where(Session.is_active.is_(True))
|
|
40
|
+
query = query.order_by(Session.last_used_at.desc())
|
|
41
|
+
result = await db.execute(query)
|
|
42
|
+
return list(result.scalars().all())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def create(
|
|
46
|
+
db: AsyncSession,
|
|
47
|
+
*,
|
|
48
|
+
user_id: UUID,
|
|
49
|
+
refresh_token_hash: str,
|
|
50
|
+
expires_at: datetime,
|
|
51
|
+
device_name: str | None = None,
|
|
52
|
+
device_type: str | None = None,
|
|
53
|
+
ip_address: str | None = None,
|
|
54
|
+
user_agent: str | None = None,
|
|
55
|
+
) -> Session:
|
|
56
|
+
"""Create a new session."""
|
|
57
|
+
session = Session(
|
|
58
|
+
user_id=user_id,
|
|
59
|
+
refresh_token_hash=refresh_token_hash,
|
|
60
|
+
expires_at=expires_at,
|
|
61
|
+
device_name=device_name,
|
|
62
|
+
device_type=device_type,
|
|
63
|
+
ip_address=ip_address,
|
|
64
|
+
user_agent=user_agent,
|
|
65
|
+
)
|
|
66
|
+
db.add(session)
|
|
67
|
+
await db.flush()
|
|
68
|
+
await db.refresh(session)
|
|
69
|
+
return session
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def update_last_used(db: AsyncSession, session_id: UUID) -> None:
|
|
73
|
+
"""Update session last used timestamp."""
|
|
74
|
+
await db.execute(
|
|
75
|
+
update(Session)
|
|
76
|
+
.where(Session.id == session_id)
|
|
77
|
+
.values(last_used_at=datetime.utcnow())
|
|
78
|
+
)
|
|
79
|
+
await db.flush()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def deactivate(db: AsyncSession, session_id: UUID) -> Session | None:
|
|
83
|
+
"""Deactivate a session (logout)."""
|
|
84
|
+
session = await get_by_id(db, session_id)
|
|
85
|
+
if session:
|
|
86
|
+
session.is_active = False
|
|
87
|
+
db.add(session)
|
|
88
|
+
await db.flush()
|
|
89
|
+
return session
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def deactivate_all_user_sessions(db: AsyncSession, user_id: UUID) -> int:
|
|
93
|
+
"""Deactivate all sessions for a user. Returns count of deactivated sessions."""
|
|
94
|
+
result = await db.execute(
|
|
95
|
+
update(Session)
|
|
96
|
+
.where(Session.user_id == user_id, Session.is_active.is_(True))
|
|
97
|
+
.values(is_active=False)
|
|
98
|
+
)
|
|
99
|
+
await db.flush()
|
|
100
|
+
return result.rowcount
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
async def deactivate_by_refresh_token_hash(db: AsyncSession, token_hash: str) -> Session | None:
|
|
104
|
+
"""Deactivate session by refresh token hash."""
|
|
105
|
+
session = await get_by_refresh_token_hash(db, token_hash)
|
|
106
|
+
if session:
|
|
107
|
+
session.is_active = False
|
|
108
|
+
db.add(session)
|
|
109
|
+
await db.flush()
|
|
110
|
+
return session
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
{%- elif cookiecutter.use_sqlite %}
|
|
114
|
+
"""Session repository (SQLite sync)."""
|
|
115
|
+
|
|
116
|
+
from datetime import datetime
|
|
117
|
+
|
|
118
|
+
from sqlalchemy import select, update
|
|
119
|
+
from sqlalchemy.orm import Session as DBSession
|
|
120
|
+
|
|
121
|
+
from app.db.models.session import Session
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_by_id(db: DBSession, session_id: str) -> Session | None:
|
|
125
|
+
"""Get session by ID."""
|
|
126
|
+
return db.get(Session, session_id)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_by_refresh_token_hash(db: DBSession, token_hash: str) -> Session | None:
|
|
130
|
+
"""Get session by refresh token hash."""
|
|
131
|
+
result = db.execute(
|
|
132
|
+
select(Session).where(
|
|
133
|
+
Session.refresh_token_hash == token_hash,
|
|
134
|
+
Session.is_active.is_(True),
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
return result.scalar_one_or_none()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_user_sessions(
|
|
141
|
+
db: DBSession,
|
|
142
|
+
user_id: str,
|
|
143
|
+
*,
|
|
144
|
+
active_only: bool = True,
|
|
145
|
+
) -> list[Session]:
|
|
146
|
+
"""Get all sessions for a user."""
|
|
147
|
+
query = select(Session).where(Session.user_id == user_id)
|
|
148
|
+
if active_only:
|
|
149
|
+
query = query.where(Session.is_active.is_(True))
|
|
150
|
+
query = query.order_by(Session.last_used_at.desc())
|
|
151
|
+
result = db.execute(query)
|
|
152
|
+
return list(result.scalars().all())
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def create(
|
|
156
|
+
db: DBSession,
|
|
157
|
+
*,
|
|
158
|
+
user_id: str,
|
|
159
|
+
refresh_token_hash: str,
|
|
160
|
+
expires_at: datetime,
|
|
161
|
+
device_name: str | None = None,
|
|
162
|
+
device_type: str | None = None,
|
|
163
|
+
ip_address: str | None = None,
|
|
164
|
+
user_agent: str | None = None,
|
|
165
|
+
) -> Session:
|
|
166
|
+
"""Create a new session."""
|
|
167
|
+
session = Session(
|
|
168
|
+
user_id=user_id,
|
|
169
|
+
refresh_token_hash=refresh_token_hash,
|
|
170
|
+
expires_at=expires_at,
|
|
171
|
+
device_name=device_name,
|
|
172
|
+
device_type=device_type,
|
|
173
|
+
ip_address=ip_address,
|
|
174
|
+
user_agent=user_agent,
|
|
175
|
+
)
|
|
176
|
+
db.add(session)
|
|
177
|
+
db.flush()
|
|
178
|
+
db.refresh(session)
|
|
179
|
+
return session
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def update_last_used(db: DBSession, session_id: str) -> None:
|
|
183
|
+
"""Update session last used timestamp."""
|
|
184
|
+
db.execute(
|
|
185
|
+
update(Session)
|
|
186
|
+
.where(Session.id == session_id)
|
|
187
|
+
.values(last_used_at=datetime.utcnow())
|
|
188
|
+
)
|
|
189
|
+
db.flush()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def deactivate(db: DBSession, session_id: str) -> Session | None:
|
|
193
|
+
"""Deactivate a session (logout)."""
|
|
194
|
+
session = get_by_id(db, session_id)
|
|
195
|
+
if session:
|
|
196
|
+
session.is_active = False
|
|
197
|
+
db.add(session)
|
|
198
|
+
db.flush()
|
|
199
|
+
return session
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def deactivate_all_user_sessions(db: DBSession, user_id: str) -> int:
|
|
203
|
+
"""Deactivate all sessions for a user. Returns count of deactivated sessions."""
|
|
204
|
+
result = db.execute(
|
|
205
|
+
update(Session)
|
|
206
|
+
.where(Session.user_id == user_id, Session.is_active.is_(True))
|
|
207
|
+
.values(is_active=False)
|
|
208
|
+
)
|
|
209
|
+
db.flush()
|
|
210
|
+
return result.rowcount
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def deactivate_by_refresh_token_hash(db: DBSession, token_hash: str) -> Session | None:
|
|
214
|
+
"""Deactivate session by refresh token hash."""
|
|
215
|
+
session = get_by_refresh_token_hash(db, token_hash)
|
|
216
|
+
if session:
|
|
217
|
+
session.is_active = False
|
|
218
|
+
db.add(session)
|
|
219
|
+
db.flush()
|
|
220
|
+
return session
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
{%- elif cookiecutter.use_mongodb %}
|
|
224
|
+
"""Session repository (MongoDB)."""
|
|
225
|
+
|
|
226
|
+
from datetime import UTC, datetime
|
|
227
|
+
|
|
228
|
+
from app.db.models.session import Session
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
async def get_by_id(session_id: str) -> Session | None:
|
|
232
|
+
"""Get session by ID."""
|
|
233
|
+
return await Session.get(session_id)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
async def get_by_refresh_token_hash(token_hash: str) -> Session | None:
|
|
237
|
+
"""Get session by refresh token hash."""
|
|
238
|
+
return await Session.find_one(
|
|
239
|
+
Session.refresh_token_hash == token_hash,
|
|
240
|
+
Session.is_active == True,
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
async def get_user_sessions(
|
|
245
|
+
user_id: str,
|
|
246
|
+
*,
|
|
247
|
+
active_only: bool = True,
|
|
248
|
+
) -> list[Session]:
|
|
249
|
+
"""Get all sessions for a user."""
|
|
250
|
+
query = Session.find(Session.user_id == user_id)
|
|
251
|
+
if active_only:
|
|
252
|
+
query = query.find(Session.is_active == True)
|
|
253
|
+
return await query.sort(-Session.last_used_at).to_list()
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
async def create(
|
|
257
|
+
*,
|
|
258
|
+
user_id: str,
|
|
259
|
+
refresh_token_hash: str,
|
|
260
|
+
expires_at: datetime,
|
|
261
|
+
device_name: str | None = None,
|
|
262
|
+
device_type: str | None = None,
|
|
263
|
+
ip_address: str | None = None,
|
|
264
|
+
user_agent: str | None = None,
|
|
265
|
+
) -> Session:
|
|
266
|
+
"""Create a new session."""
|
|
267
|
+
session = Session(
|
|
268
|
+
user_id=user_id,
|
|
269
|
+
refresh_token_hash=refresh_token_hash,
|
|
270
|
+
expires_at=expires_at,
|
|
271
|
+
device_name=device_name,
|
|
272
|
+
device_type=device_type,
|
|
273
|
+
ip_address=ip_address,
|
|
274
|
+
user_agent=user_agent,
|
|
275
|
+
)
|
|
276
|
+
await session.insert()
|
|
277
|
+
return session
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
async def update_last_used(session_id: str) -> None:
|
|
281
|
+
"""Update session last used timestamp."""
|
|
282
|
+
session = await get_by_id(session_id)
|
|
283
|
+
if session:
|
|
284
|
+
session.last_used_at = datetime.now(UTC)
|
|
285
|
+
await session.save()
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
async def deactivate(session_id: str) -> Session | None:
|
|
289
|
+
"""Deactivate a session (logout)."""
|
|
290
|
+
session = await get_by_id(session_id)
|
|
291
|
+
if session:
|
|
292
|
+
session.is_active = False
|
|
293
|
+
await session.save()
|
|
294
|
+
return session
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
async def deactivate_all_user_sessions(user_id: str) -> int:
|
|
298
|
+
"""Deactivate all sessions for a user. Returns count of deactivated sessions."""
|
|
299
|
+
result = await Session.find(
|
|
300
|
+
Session.user_id == user_id,
|
|
301
|
+
Session.is_active == True,
|
|
302
|
+
).update({"$set": {"is_active": False}})
|
|
303
|
+
return result.modified_count if result else 0
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
async def deactivate_by_refresh_token_hash(token_hash: str) -> Session | None:
|
|
307
|
+
"""Deactivate session by refresh token hash."""
|
|
308
|
+
session = await get_by_refresh_token_hash(token_hash)
|
|
309
|
+
if session:
|
|
310
|
+
session.is_active = False
|
|
311
|
+
await session.save()
|
|
312
|
+
return session
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
{%- endif %}
|
|
316
|
+
{%- else %}
|
|
317
|
+
"""Session repository - not configured."""
|
|
318
|
+
{%- endif %}
|