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,168 @@
|
|
|
1
|
+
{%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
|
|
2
|
+
"""Session management routes."""
|
|
3
|
+
|
|
4
|
+
from fastapi import APIRouter, status
|
|
5
|
+
{%- if cookiecutter.use_postgresql %}
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
{%- endif %}
|
|
8
|
+
|
|
9
|
+
from app.api.deps import CurrentUser, SessionSvc
|
|
10
|
+
from app.schemas.session import LogoutAllResponse, SessionListResponse, SessionRead
|
|
11
|
+
|
|
12
|
+
router = APIRouter()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
{%- if cookiecutter.use_postgresql %}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@router.get("", response_model=SessionListResponse)
|
|
19
|
+
async def list_sessions(
|
|
20
|
+
current_user: CurrentUser,
|
|
21
|
+
session_service: SessionSvc,
|
|
22
|
+
):
|
|
23
|
+
"""Get all active sessions for the current user."""
|
|
24
|
+
sessions = await session_service.get_user_sessions(current_user.id)
|
|
25
|
+
return SessionListResponse(
|
|
26
|
+
sessions=[
|
|
27
|
+
SessionRead(
|
|
28
|
+
id=s.id,
|
|
29
|
+
device_name=s.device_name,
|
|
30
|
+
device_type=s.device_type,
|
|
31
|
+
ip_address=s.ip_address,
|
|
32
|
+
is_current=False, # TODO: compare with current session
|
|
33
|
+
created_at=s.created_at,
|
|
34
|
+
last_used_at=s.last_used_at,
|
|
35
|
+
)
|
|
36
|
+
for s in sessions
|
|
37
|
+
],
|
|
38
|
+
total=len(sessions),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@router.delete("/{session_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
43
|
+
async def logout_session(
|
|
44
|
+
session_id: UUID,
|
|
45
|
+
current_user: CurrentUser,
|
|
46
|
+
session_service: SessionSvc,
|
|
47
|
+
):
|
|
48
|
+
"""Logout a specific session."""
|
|
49
|
+
await session_service.logout_session(session_id, current_user.id)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@router.delete("", response_model=LogoutAllResponse)
|
|
53
|
+
async def logout_all_sessions(
|
|
54
|
+
current_user: CurrentUser,
|
|
55
|
+
session_service: SessionSvc,
|
|
56
|
+
):
|
|
57
|
+
"""Logout from all sessions (logout from all devices)."""
|
|
58
|
+
count = await session_service.logout_all_sessions(current_user.id)
|
|
59
|
+
return LogoutAllResponse(
|
|
60
|
+
message="Successfully logged out from all sessions",
|
|
61
|
+
sessions_logged_out=count,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
{%- elif cookiecutter.use_mongodb %}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@router.get("", response_model=SessionListResponse)
|
|
69
|
+
async def list_sessions(
|
|
70
|
+
current_user: CurrentUser,
|
|
71
|
+
session_service: SessionSvc,
|
|
72
|
+
):
|
|
73
|
+
"""Get all active sessions for the current user."""
|
|
74
|
+
sessions = await session_service.get_user_sessions(str(current_user.id))
|
|
75
|
+
return SessionListResponse(
|
|
76
|
+
sessions=[
|
|
77
|
+
SessionRead(
|
|
78
|
+
id=str(s.id),
|
|
79
|
+
device_name=s.device_name,
|
|
80
|
+
device_type=s.device_type,
|
|
81
|
+
ip_address=s.ip_address,
|
|
82
|
+
is_current=False,
|
|
83
|
+
created_at=s.created_at,
|
|
84
|
+
last_used_at=s.last_used_at,
|
|
85
|
+
)
|
|
86
|
+
for s in sessions
|
|
87
|
+
],
|
|
88
|
+
total=len(sessions),
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@router.delete("/{session_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
93
|
+
async def logout_session(
|
|
94
|
+
session_id: str,
|
|
95
|
+
current_user: CurrentUser,
|
|
96
|
+
session_service: SessionSvc,
|
|
97
|
+
):
|
|
98
|
+
"""Logout a specific session."""
|
|
99
|
+
await session_service.logout_session(session_id, str(current_user.id))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@router.delete("", response_model=LogoutAllResponse)
|
|
103
|
+
async def logout_all_sessions(
|
|
104
|
+
current_user: CurrentUser,
|
|
105
|
+
session_service: SessionSvc,
|
|
106
|
+
):
|
|
107
|
+
"""Logout from all sessions (logout from all devices)."""
|
|
108
|
+
count = await session_service.logout_all_sessions(str(current_user.id))
|
|
109
|
+
return LogoutAllResponse(
|
|
110
|
+
message="Successfully logged out from all sessions",
|
|
111
|
+
sessions_logged_out=count,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
{%- elif cookiecutter.use_sqlite %}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@router.get("", response_model=SessionListResponse)
|
|
119
|
+
def list_sessions(
|
|
120
|
+
current_user: CurrentUser,
|
|
121
|
+
session_service: SessionSvc,
|
|
122
|
+
):
|
|
123
|
+
"""Get all active sessions for the current user."""
|
|
124
|
+
sessions = session_service.get_user_sessions(current_user.id)
|
|
125
|
+
return SessionListResponse(
|
|
126
|
+
sessions=[
|
|
127
|
+
SessionRead(
|
|
128
|
+
id=s.id,
|
|
129
|
+
device_name=s.device_name,
|
|
130
|
+
device_type=s.device_type,
|
|
131
|
+
ip_address=s.ip_address,
|
|
132
|
+
is_current=False,
|
|
133
|
+
created_at=s.created_at,
|
|
134
|
+
last_used_at=s.last_used_at,
|
|
135
|
+
)
|
|
136
|
+
for s in sessions
|
|
137
|
+
],
|
|
138
|
+
total=len(sessions),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@router.delete("/{session_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
143
|
+
def logout_session(
|
|
144
|
+
session_id: str,
|
|
145
|
+
current_user: CurrentUser,
|
|
146
|
+
session_service: SessionSvc,
|
|
147
|
+
):
|
|
148
|
+
"""Logout a specific session."""
|
|
149
|
+
session_service.logout_session(session_id, current_user.id)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@router.delete("", response_model=LogoutAllResponse)
|
|
153
|
+
def logout_all_sessions(
|
|
154
|
+
current_user: CurrentUser,
|
|
155
|
+
session_service: SessionSvc,
|
|
156
|
+
):
|
|
157
|
+
"""Logout from all sessions (logout from all devices)."""
|
|
158
|
+
count = session_service.logout_all_sessions(current_user.id)
|
|
159
|
+
return LogoutAllResponse(
|
|
160
|
+
message="Successfully logged out from all sessions",
|
|
161
|
+
sessions_logged_out=count,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
{%- endif %}
|
|
166
|
+
{%- else %}
|
|
167
|
+
"""Session routes - not configured."""
|
|
168
|
+
{%- endif %}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
{%- if cookiecutter.use_jwt %}
|
|
2
|
+
# ruff: noqa: I001 - Imports structured for Jinja2 template conditionals
|
|
3
|
+
"""User management routes."""
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
{%- if cookiecutter.use_postgresql %}
|
|
7
|
+
|
|
8
|
+
from uuid import UUID
|
|
9
|
+
{%- endif %}
|
|
10
|
+
|
|
11
|
+
from fastapi import APIRouter, Depends, status
|
|
12
|
+
{%- if cookiecutter.enable_pagination %}
|
|
13
|
+
from fastapi_pagination import Page
|
|
14
|
+
from fastapi_pagination.ext.sqlalchemy import paginate
|
|
15
|
+
from sqlalchemy import select
|
|
16
|
+
{%- endif %}
|
|
17
|
+
|
|
18
|
+
from app.api.deps import (
|
|
19
|
+
DBSession,
|
|
20
|
+
RoleChecker,
|
|
21
|
+
UserSvc,
|
|
22
|
+
get_current_user,
|
|
23
|
+
)
|
|
24
|
+
from app.db.models.user import User, UserRole
|
|
25
|
+
from app.schemas.user import UserRead, UserUpdate
|
|
26
|
+
|
|
27
|
+
router = APIRouter()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
{%- if cookiecutter.use_postgresql %}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@router.get("/me", response_model=UserRead)
|
|
34
|
+
async def read_current_user(
|
|
35
|
+
current_user: Annotated[User, Depends(get_current_user)],
|
|
36
|
+
):
|
|
37
|
+
"""Get current user.
|
|
38
|
+
|
|
39
|
+
Returns the authenticated user's profile including their role.
|
|
40
|
+
"""
|
|
41
|
+
return current_user
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@router.patch("/me", response_model=UserRead)
|
|
45
|
+
async def update_current_user(
|
|
46
|
+
user_in: UserUpdate,
|
|
47
|
+
current_user: Annotated[User, Depends(get_current_user)],
|
|
48
|
+
user_service: UserSvc,
|
|
49
|
+
):
|
|
50
|
+
"""Update current user.
|
|
51
|
+
|
|
52
|
+
Users can update their own profile (email, full_name).
|
|
53
|
+
Role changes require admin privileges.
|
|
54
|
+
"""
|
|
55
|
+
# Prevent non-admin users from changing their own role
|
|
56
|
+
if user_in.role is not None and not current_user.has_role(UserRole.ADMIN):
|
|
57
|
+
user_in.role = None
|
|
58
|
+
user = await user_service.update(current_user.id, user_in)
|
|
59
|
+
return user
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
{%- if cookiecutter.enable_pagination %}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@router.get("", response_model=Page[UserRead])
|
|
66
|
+
async def read_users(
|
|
67
|
+
db: DBSession,
|
|
68
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
69
|
+
):
|
|
70
|
+
"""Get all users (admin only)."""
|
|
71
|
+
return await paginate(db, select(User))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
{%- else %}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@router.get("", response_model=list[UserRead])
|
|
78
|
+
async def read_users(
|
|
79
|
+
user_service: UserSvc,
|
|
80
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
81
|
+
skip: int = 0,
|
|
82
|
+
limit: int = 100,
|
|
83
|
+
):
|
|
84
|
+
"""Get all users (admin only)."""
|
|
85
|
+
users = await user_service.get_multi(skip=skip, limit=limit)
|
|
86
|
+
return users
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
{%- endif %}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@router.get("/{user_id}", response_model=UserRead)
|
|
93
|
+
async def read_user(
|
|
94
|
+
user_id: UUID,
|
|
95
|
+
user_service: UserSvc,
|
|
96
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
97
|
+
):
|
|
98
|
+
"""Get user by ID (admin only).
|
|
99
|
+
|
|
100
|
+
Raises NotFoundError if user does not exist.
|
|
101
|
+
"""
|
|
102
|
+
user = await user_service.get_by_id(user_id)
|
|
103
|
+
return user
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@router.patch("/{user_id}", response_model=UserRead)
|
|
107
|
+
async def update_user_by_id(
|
|
108
|
+
user_id: UUID,
|
|
109
|
+
user_in: UserUpdate,
|
|
110
|
+
user_service: UserSvc,
|
|
111
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
112
|
+
):
|
|
113
|
+
"""Update user by ID (admin only).
|
|
114
|
+
|
|
115
|
+
Admins can update any user including their role.
|
|
116
|
+
|
|
117
|
+
Raises NotFoundError if user does not exist.
|
|
118
|
+
"""
|
|
119
|
+
user = await user_service.update(user_id, user_in)
|
|
120
|
+
return user
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
124
|
+
async def delete_user_by_id(
|
|
125
|
+
user_id: UUID,
|
|
126
|
+
user_service: UserSvc,
|
|
127
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
128
|
+
):
|
|
129
|
+
"""Delete user by ID (admin only).
|
|
130
|
+
|
|
131
|
+
Raises NotFoundError if user does not exist.
|
|
132
|
+
"""
|
|
133
|
+
await user_service.delete(user_id)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
{%- elif cookiecutter.use_mongodb %}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@router.get("/me", response_model=UserRead)
|
|
140
|
+
async def read_current_user(
|
|
141
|
+
current_user: Annotated[User, Depends(get_current_user)],
|
|
142
|
+
):
|
|
143
|
+
"""Get current user.
|
|
144
|
+
|
|
145
|
+
Returns the authenticated user's profile including their role.
|
|
146
|
+
"""
|
|
147
|
+
return current_user
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@router.patch("/me", response_model=UserRead)
|
|
151
|
+
async def update_current_user(
|
|
152
|
+
user_in: UserUpdate,
|
|
153
|
+
current_user: Annotated[User, Depends(get_current_user)],
|
|
154
|
+
user_service: UserSvc,
|
|
155
|
+
):
|
|
156
|
+
"""Update current user.
|
|
157
|
+
|
|
158
|
+
Users can update their own profile (email, full_name).
|
|
159
|
+
Role changes require admin privileges.
|
|
160
|
+
"""
|
|
161
|
+
# Prevent non-admin users from changing their own role
|
|
162
|
+
if user_in.role is not None and not current_user.has_role(UserRole.ADMIN):
|
|
163
|
+
user_in.role = None
|
|
164
|
+
user = await user_service.update(str(current_user.id), user_in)
|
|
165
|
+
return user
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@router.get("", response_model=list[UserRead])
|
|
169
|
+
async def read_users(
|
|
170
|
+
user_service: UserSvc,
|
|
171
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
172
|
+
skip: int = 0,
|
|
173
|
+
limit: int = 100,
|
|
174
|
+
):
|
|
175
|
+
"""Get all users (admin only)."""
|
|
176
|
+
users = await user_service.get_multi(skip=skip, limit=limit)
|
|
177
|
+
return users
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@router.get("/{user_id}", response_model=UserRead)
|
|
181
|
+
async def read_user(
|
|
182
|
+
user_id: str,
|
|
183
|
+
user_service: UserSvc,
|
|
184
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
185
|
+
):
|
|
186
|
+
"""Get user by ID (admin only).
|
|
187
|
+
|
|
188
|
+
Raises NotFoundError if user does not exist.
|
|
189
|
+
"""
|
|
190
|
+
user = await user_service.get_by_id(user_id)
|
|
191
|
+
return user
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@router.patch("/{user_id}", response_model=UserRead)
|
|
195
|
+
async def update_user_by_id(
|
|
196
|
+
user_id: str,
|
|
197
|
+
user_in: UserUpdate,
|
|
198
|
+
user_service: UserSvc,
|
|
199
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
200
|
+
):
|
|
201
|
+
"""Update user by ID (admin only).
|
|
202
|
+
|
|
203
|
+
Admins can update any user including their role.
|
|
204
|
+
|
|
205
|
+
Raises NotFoundError if user does not exist.
|
|
206
|
+
"""
|
|
207
|
+
user = await user_service.update(user_id, user_in)
|
|
208
|
+
return user
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
212
|
+
async def delete_user_by_id(
|
|
213
|
+
user_id: str,
|
|
214
|
+
user_service: UserSvc,
|
|
215
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
216
|
+
):
|
|
217
|
+
"""Delete user by ID (admin only).
|
|
218
|
+
|
|
219
|
+
Raises NotFoundError if user does not exist.
|
|
220
|
+
"""
|
|
221
|
+
await user_service.delete(user_id)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
{%- elif cookiecutter.use_sqlite %}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@router.get("/me", response_model=UserRead)
|
|
228
|
+
def read_current_user(
|
|
229
|
+
current_user: Annotated[User, Depends(get_current_user)],
|
|
230
|
+
):
|
|
231
|
+
"""Get current user.
|
|
232
|
+
|
|
233
|
+
Returns the authenticated user's profile including their role.
|
|
234
|
+
"""
|
|
235
|
+
return current_user
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@router.patch("/me", response_model=UserRead)
|
|
239
|
+
def update_current_user(
|
|
240
|
+
user_in: UserUpdate,
|
|
241
|
+
current_user: Annotated[User, Depends(get_current_user)],
|
|
242
|
+
user_service: UserSvc,
|
|
243
|
+
):
|
|
244
|
+
"""Update current user.
|
|
245
|
+
|
|
246
|
+
Users can update their own profile (email, full_name).
|
|
247
|
+
Role changes require admin privileges.
|
|
248
|
+
"""
|
|
249
|
+
# Prevent non-admin users from changing their own role
|
|
250
|
+
if user_in.role is not None and not current_user.has_role(UserRole.ADMIN):
|
|
251
|
+
user_in.role = None
|
|
252
|
+
user = user_service.update(current_user.id, user_in)
|
|
253
|
+
return user
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
{%- if cookiecutter.enable_pagination %}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@router.get("", response_model=Page[UserRead])
|
|
260
|
+
def read_users(
|
|
261
|
+
db: DBSession,
|
|
262
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
263
|
+
):
|
|
264
|
+
"""Get all users (admin only)."""
|
|
265
|
+
return paginate(db, select(User))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
{%- else %}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@router.get("", response_model=list[UserRead])
|
|
272
|
+
def read_users(
|
|
273
|
+
user_service: UserSvc,
|
|
274
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
275
|
+
skip: int = 0,
|
|
276
|
+
limit: int = 100,
|
|
277
|
+
):
|
|
278
|
+
"""Get all users (admin only)."""
|
|
279
|
+
users = user_service.get_multi(skip=skip, limit=limit)
|
|
280
|
+
return users
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
{%- endif %}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@router.get("/{user_id}", response_model=UserRead)
|
|
287
|
+
def read_user(
|
|
288
|
+
user_id: str,
|
|
289
|
+
user_service: UserSvc,
|
|
290
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
291
|
+
):
|
|
292
|
+
"""Get user by ID (admin only).
|
|
293
|
+
|
|
294
|
+
Raises NotFoundError if user does not exist.
|
|
295
|
+
"""
|
|
296
|
+
user = user_service.get_by_id(user_id)
|
|
297
|
+
return user
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@router.patch("/{user_id}", response_model=UserRead)
|
|
301
|
+
def update_user_by_id(
|
|
302
|
+
user_id: str,
|
|
303
|
+
user_in: UserUpdate,
|
|
304
|
+
user_service: UserSvc,
|
|
305
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
306
|
+
):
|
|
307
|
+
"""Update user by ID (admin only).
|
|
308
|
+
|
|
309
|
+
Admins can update any user including their role.
|
|
310
|
+
|
|
311
|
+
Raises NotFoundError if user does not exist.
|
|
312
|
+
"""
|
|
313
|
+
user = user_service.update(user_id, user_in)
|
|
314
|
+
return user
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
318
|
+
def delete_user_by_id(
|
|
319
|
+
user_id: str,
|
|
320
|
+
user_service: UserSvc,
|
|
321
|
+
current_user: Annotated[User, Depends(RoleChecker(UserRole.ADMIN))],
|
|
322
|
+
):
|
|
323
|
+
"""Delete user by ID (admin only).
|
|
324
|
+
|
|
325
|
+
Raises NotFoundError if user does not exist.
|
|
326
|
+
"""
|
|
327
|
+
user_service.delete(user_id)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
{%- endif %}
|
|
331
|
+
{%- else %}
|
|
332
|
+
"""User routes - not configured."""
|
|
333
|
+
{%- endif %}
|