fastapi-fullstack 0.1.2__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.
Files changed (221) hide show
  1. fastapi_fullstack-0.1.2.dist-info/METADATA +545 -0
  2. fastapi_fullstack-0.1.2.dist-info/RECORD +221 -0
  3. fastapi_fullstack-0.1.2.dist-info/WHEEL +4 -0
  4. fastapi_fullstack-0.1.2.dist-info/entry_points.txt +2 -0
  5. fastapi_fullstack-0.1.2.dist-info/licenses/LICENSE +21 -0
  6. fastapi_gen/__init__.py +3 -0
  7. fastapi_gen/cli.py +256 -0
  8. fastapi_gen/config.py +255 -0
  9. fastapi_gen/generator.py +181 -0
  10. fastapi_gen/prompts.py +648 -0
  11. fastapi_gen/template/cookiecutter.json +76 -0
  12. fastapi_gen/template/hooks/post_gen_project.py +111 -0
  13. fastapi_gen/template/{{cookiecutter.project_slug}}/.env.example +136 -0
  14. fastapi_gen/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +150 -0
  15. fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore +108 -0
  16. fastapi_gen/template/{{cookiecutter.project_slug}}/CLAUDE.md +357 -0
  17. fastapi_gen/template/{{cookiecutter.project_slug}}/Makefile +298 -0
  18. fastapi_gen/template/{{cookiecutter.project_slug}}/README.md +723 -0
  19. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.dockerignore +60 -0
  20. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +32 -0
  21. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile +56 -0
  22. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +76 -0
  23. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +30 -0
  24. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
  25. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini +48 -0
  26. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +3 -0
  27. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +115 -0
  28. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +13 -0
  29. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +202 -0
  30. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +13 -0
  31. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +17 -0
  32. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +1 -0
  33. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +528 -0
  34. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +85 -0
  35. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +10 -0
  36. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +9 -0
  37. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +87 -0
  38. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +448 -0
  39. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +395 -0
  40. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +490 -0
  41. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +227 -0
  42. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +275 -0
  43. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +205 -0
  44. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +168 -0
  45. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +333 -0
  46. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +477 -0
  47. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +46 -0
  48. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +221 -0
  49. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +14 -0
  50. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +88 -0
  51. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +117 -0
  52. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +75 -0
  53. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +28 -0
  54. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +266 -0
  55. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +5 -0
  56. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +23 -0
  57. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +247 -0
  58. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +153 -0
  59. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +122 -0
  60. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +101 -0
  61. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +99 -0
  62. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +23 -0
  63. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +58 -0
  64. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +271 -0
  65. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +102 -0
  66. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +7 -0
  67. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +41 -0
  68. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +31 -0
  69. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +319 -0
  70. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +96 -0
  71. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +126 -0
  72. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +218 -0
  73. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +244 -0
  74. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +113 -0
  75. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +326 -0
  76. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +9 -0
  77. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +73 -0
  78. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +49 -0
  79. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +154 -0
  80. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +760 -0
  81. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +222 -0
  82. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +318 -0
  83. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +322 -0
  84. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +358 -0
  85. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +50 -0
  86. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +57 -0
  87. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +195 -0
  88. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +52 -0
  89. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +42 -0
  90. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +31 -0
  91. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +64 -0
  92. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +89 -0
  93. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +38 -0
  94. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +797 -0
  95. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +246 -0
  96. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +333 -0
  97. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +432 -0
  98. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +561 -0
  99. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +5 -0
  100. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +64 -0
  101. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +38 -0
  102. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +25 -0
  103. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +106 -0
  104. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +29 -0
  105. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +92 -0
  106. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +1 -0
  107. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +438 -0
  108. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +158 -0
  109. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
  110. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +1 -0
  111. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +1 -0
  112. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +242 -0
  113. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +151 -0
  114. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +113 -0
  115. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +310 -0
  116. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +253 -0
  117. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +151 -0
  118. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +121 -0
  119. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +183 -0
  120. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +173 -0
  121. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +143 -0
  122. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +118 -0
  123. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +181 -0
  124. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +124 -0
  125. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +363 -0
  126. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +85 -0
  127. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +242 -0
  128. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +31 -0
  129. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +382 -0
  130. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.yml +241 -0
  131. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.env.example +12 -0
  132. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.gitignore +45 -0
  133. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +19 -0
  134. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +11 -0
  135. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +44 -0
  136. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/README.md +693 -0
  137. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +49 -0
  138. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +134 -0
  139. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +207 -0
  140. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +73 -0
  141. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +14 -0
  142. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +84 -0
  143. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +84 -0
  144. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +76 -0
  145. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/package.json +66 -0
  146. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +101 -0
  147. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +7 -0
  148. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/layout.tsx +11 -0
  149. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/login/page.tsx +5 -0
  150. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/register/page.tsx +5 -0
  151. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/chat/page.tsx +20 -0
  152. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/dashboard/page.tsx +99 -0
  153. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/layout.tsx +17 -0
  154. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/profile/page.tsx +156 -0
  155. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +58 -0
  156. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +24 -0
  157. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +39 -0
  158. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +50 -0
  159. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +54 -0
  160. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +26 -0
  161. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +41 -0
  162. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +108 -0
  163. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +73 -0
  164. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +21 -0
  165. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/auth/callback/page.tsx +96 -0
  166. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +108 -0
  167. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +25 -0
  168. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/page.tsx +73 -0
  169. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +29 -0
  170. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +2 -0
  171. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +120 -0
  172. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +153 -0
  173. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +135 -0
  174. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +73 -0
  175. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +261 -0
  176. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +8 -0
  177. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +63 -0
  178. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +18 -0
  179. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +60 -0
  180. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +32 -0
  181. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +3 -0
  182. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +97 -0
  183. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +45 -0
  184. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +2 -0
  185. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +48 -0
  186. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +7 -0
  187. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +53 -0
  188. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +83 -0
  189. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +35 -0
  190. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +75 -0
  191. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +54 -0
  192. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +82 -0
  193. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +12 -0
  194. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +21 -0
  195. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +21 -0
  196. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +6 -0
  197. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +97 -0
  198. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +203 -0
  199. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +175 -0
  200. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +105 -0
  201. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +32 -0
  202. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +90 -0
  203. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +39 -0
  204. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +78 -0
  205. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +44 -0
  206. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +44 -0
  207. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +33 -0
  208. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +72 -0
  209. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +48 -0
  210. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +65 -0
  211. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +76 -0
  212. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +6 -0
  213. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +44 -0
  214. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +27 -0
  215. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +52 -0
  216. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +81 -0
  217. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +49 -0
  218. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +10 -0
  219. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +28 -0
  220. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +36 -0
  221. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +56 -0
@@ -0,0 +1,275 @@
1
+ {%- if cookiecutter.include_example_crud and cookiecutter.use_database %}
2
+ # ruff: noqa: I001 - Imports structured for Jinja2 template conditionals
3
+ """Item CRUD routes - example API endpoints.
4
+
5
+ This module demonstrates a complete CRUD API for the Item entity.
6
+ You can use it as a template for creating your own endpoints.
7
+
8
+ The endpoints are:
9
+ - GET /items - List all items (with pagination if enabled)
10
+ - POST /items - Create a new item
11
+ - GET /items/{item_id} - Get a single item by ID
12
+ - PATCH /items/{item_id} - Update an item
13
+ - DELETE /items/{item_id} - Delete an item
14
+ """
15
+ {%- if cookiecutter.use_postgresql %}
16
+
17
+ from uuid import UUID
18
+ {%- endif %}
19
+
20
+ from fastapi import APIRouter, status
21
+ {%- if cookiecutter.enable_pagination and cookiecutter.use_postgresql %}
22
+ from fastapi_pagination import Page
23
+ from fastapi_pagination.ext.sqlalchemy import paginate
24
+ from sqlalchemy import select
25
+ {%- elif cookiecutter.enable_pagination and cookiecutter.use_sqlite %}
26
+ from fastapi_pagination import Page
27
+ from fastapi_pagination.ext.sqlalchemy import paginate
28
+ from sqlalchemy import select
29
+ {%- endif %}
30
+
31
+ {%- if cookiecutter.use_mongodb %}
32
+ from app.api.deps import ItemSvc
33
+ {%- elif cookiecutter.enable_pagination %}
34
+ from app.api.deps import DBSession, ItemSvc
35
+ {%- else %}
36
+ from app.api.deps import ItemSvc
37
+ {%- endif %}
38
+ {%- if cookiecutter.enable_pagination and (cookiecutter.use_postgresql or cookiecutter.use_sqlite) %}
39
+ from app.db.models.item import Item
40
+ {%- endif %}
41
+ from app.schemas.item import ItemCreate, ItemRead, ItemUpdate
42
+
43
+ router = APIRouter()
44
+
45
+
46
+ {%- if cookiecutter.use_postgresql %}
47
+
48
+ {%- if cookiecutter.enable_pagination %}
49
+
50
+
51
+ @router.get("", response_model=Page[ItemRead])
52
+ async def list_items(db: DBSession):
53
+ """List all items with pagination.
54
+
55
+ Returns a paginated list of items. Use query parameters
56
+ `page` and `size` to control pagination.
57
+ """
58
+ return await paginate(db, select(Item))
59
+ {%- else %}
60
+
61
+
62
+ @router.get("", response_model=list[ItemRead])
63
+ async def list_items(
64
+ item_service: ItemSvc,
65
+ skip: int = 0,
66
+ limit: int = 100,
67
+ ):
68
+ """List all items.
69
+
70
+ Returns a list of items with offset-based pagination.
71
+ """
72
+ return await item_service.get_multi(skip=skip, limit=limit)
73
+ {%- endif %}
74
+
75
+
76
+ @router.post("", response_model=ItemRead, status_code=status.HTTP_201_CREATED)
77
+ async def create_item(
78
+ item_in: ItemCreate,
79
+ item_service: ItemSvc,
80
+ ):
81
+ """Create a new item.
82
+
83
+ Creates an item with the provided title and optional description.
84
+ """
85
+ return await item_service.create(item_in)
86
+
87
+
88
+ @router.get("/{item_id}", response_model=ItemRead)
89
+ async def get_item(
90
+ item_id: UUID,
91
+ item_service: ItemSvc,
92
+ ):
93
+ """Get a single item by ID.
94
+
95
+ Raises 404 if the item does not exist.
96
+ """
97
+ return await item_service.get_by_id(item_id)
98
+
99
+
100
+ @router.patch("/{item_id}", response_model=ItemRead)
101
+ async def update_item(
102
+ item_id: UUID,
103
+ item_in: ItemUpdate,
104
+ item_service: ItemSvc,
105
+ ):
106
+ """Update an item.
107
+
108
+ Supports partial updates - only provided fields are updated.
109
+ Raises 404 if the item does not exist.
110
+ """
111
+ return await item_service.update(item_id, item_in)
112
+
113
+
114
+ @router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
115
+ async def delete_item(
116
+ item_id: UUID,
117
+ item_service: ItemSvc,
118
+ ):
119
+ """Delete an item.
120
+
121
+ Raises 404 if the item does not exist.
122
+ """
123
+ await item_service.delete(item_id)
124
+
125
+
126
+ {%- elif cookiecutter.use_sqlite %}
127
+
128
+ {%- if cookiecutter.enable_pagination %}
129
+
130
+
131
+ @router.get("", response_model=Page[ItemRead])
132
+ def list_items(db: DBSession):
133
+ """List all items with pagination.
134
+
135
+ Returns a paginated list of items. Use query parameters
136
+ `page` and `size` to control pagination.
137
+ """
138
+ return paginate(db, select(Item))
139
+ {%- else %}
140
+
141
+
142
+ @router.get("", response_model=list[ItemRead])
143
+ def list_items(
144
+ item_service: ItemSvc,
145
+ skip: int = 0,
146
+ limit: int = 100,
147
+ ):
148
+ """List all items.
149
+
150
+ Returns a list of items with offset-based pagination.
151
+ """
152
+ return item_service.get_multi(skip=skip, limit=limit)
153
+ {%- endif %}
154
+
155
+
156
+ @router.post("", response_model=ItemRead, status_code=status.HTTP_201_CREATED)
157
+ def create_item(
158
+ item_in: ItemCreate,
159
+ item_service: ItemSvc,
160
+ ):
161
+ """Create a new item.
162
+
163
+ Creates an item with the provided title and optional description.
164
+ """
165
+ return item_service.create(item_in)
166
+
167
+
168
+ @router.get("/{item_id}", response_model=ItemRead)
169
+ def get_item(
170
+ item_id: str,
171
+ item_service: ItemSvc,
172
+ ):
173
+ """Get a single item by ID.
174
+
175
+ Raises 404 if the item does not exist.
176
+ """
177
+ return item_service.get_by_id(item_id)
178
+
179
+
180
+ @router.patch("/{item_id}", response_model=ItemRead)
181
+ def update_item(
182
+ item_id: str,
183
+ item_in: ItemUpdate,
184
+ item_service: ItemSvc,
185
+ ):
186
+ """Update an item.
187
+
188
+ Supports partial updates - only provided fields are updated.
189
+ Raises 404 if the item does not exist.
190
+ """
191
+ return item_service.update(item_id, item_in)
192
+
193
+
194
+ @router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
195
+ def delete_item(
196
+ item_id: str,
197
+ item_service: ItemSvc,
198
+ ):
199
+ """Delete an item.
200
+
201
+ Raises 404 if the item does not exist.
202
+ """
203
+ item_service.delete(item_id)
204
+
205
+
206
+ {%- elif cookiecutter.use_mongodb %}
207
+
208
+
209
+ @router.get("", response_model=list[ItemRead])
210
+ async def list_items(
211
+ item_service: ItemSvc,
212
+ skip: int = 0,
213
+ limit: int = 100,
214
+ ):
215
+ """List all items.
216
+
217
+ Returns a list of items with offset-based pagination.
218
+ """
219
+ return await item_service.get_multi(skip=skip, limit=limit)
220
+
221
+
222
+ @router.post("", response_model=ItemRead, status_code=status.HTTP_201_CREATED)
223
+ async def create_item(
224
+ item_in: ItemCreate,
225
+ item_service: ItemSvc,
226
+ ):
227
+ """Create a new item.
228
+
229
+ Creates an item with the provided title and optional description.
230
+ """
231
+ return await item_service.create(item_in)
232
+
233
+
234
+ @router.get("/{item_id}", response_model=ItemRead)
235
+ async def get_item(
236
+ item_id: str,
237
+ item_service: ItemSvc,
238
+ ):
239
+ """Get a single item by ID.
240
+
241
+ Raises 404 if the item does not exist.
242
+ """
243
+ return await item_service.get_by_id(item_id)
244
+
245
+
246
+ @router.patch("/{item_id}", response_model=ItemRead)
247
+ async def update_item(
248
+ item_id: str,
249
+ item_in: ItemUpdate,
250
+ item_service: ItemSvc,
251
+ ):
252
+ """Update an item.
253
+
254
+ Supports partial updates - only provided fields are updated.
255
+ Raises 404 if the item does not exist.
256
+ """
257
+ return await item_service.update(item_id, item_in)
258
+
259
+
260
+ @router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
261
+ async def delete_item(
262
+ item_id: str,
263
+ item_service: ItemSvc,
264
+ ):
265
+ """Delete an item.
266
+
267
+ Raises 404 if the item does not exist.
268
+ """
269
+ await item_service.delete(item_id)
270
+
271
+
272
+ {%- endif %}
273
+ {%- else %}
274
+ """Item routes - not configured."""
275
+ {%- endif %}
@@ -0,0 +1,205 @@
1
+ {%- if cookiecutter.enable_oauth %}
2
+ """OAuth2 authentication routes."""
3
+
4
+ from urllib.parse import urlencode
5
+
6
+ from fastapi import APIRouter, Request
7
+ from fastapi.responses import RedirectResponse
8
+
9
+ from app.api.deps import UserSvc
10
+ from app.core.oauth import oauth
11
+ from app.core.security import create_access_token, create_refresh_token
12
+
13
+ router = APIRouter()
14
+
15
+ # Frontend URL for OAuth callback redirect
16
+ FRONTEND_URL = "http://localhost:{{ cookiecutter.frontend_port }}"
17
+
18
+ {%- if cookiecutter.enable_oauth_google %}
19
+
20
+
21
+ @router.get("/google/login")
22
+ async def google_login(request: Request):
23
+ """Redirect to Google OAuth2 login page."""
24
+ from app.core.config import settings
25
+
26
+ redirect_uri = settings.GOOGLE_REDIRECT_URI
27
+ return await oauth.google.authorize_redirect(request, redirect_uri)
28
+
29
+
30
+ {%- if cookiecutter.use_postgresql %}
31
+
32
+
33
+ @router.get("/google/callback")
34
+ async def google_callback(request: Request, user_service: UserSvc):
35
+ """Handle Google OAuth2 callback.
36
+
37
+ Creates a new user if one doesn't exist with the Google email,
38
+ or returns tokens for existing user. Redirects to frontend with tokens.
39
+ """
40
+ try:
41
+ token = await oauth.google.authorize_access_token(request)
42
+ user_info = token.get("userinfo")
43
+
44
+ if not user_info:
45
+ params = urlencode({"error": "Failed to get user info from Google"})
46
+ return RedirectResponse(url=f"{FRONTEND_URL}/login?{params}")
47
+
48
+ email = user_info.get("email")
49
+ google_id = user_info.get("sub")
50
+ full_name = user_info.get("name")
51
+
52
+ # Try to find existing user by OAuth ID
53
+ user = await user_service.get_by_oauth("google", google_id)
54
+
55
+ if not user:
56
+ # Try to find by email (link existing account)
57
+ user = await user_service.get_by_email(email)
58
+ if user:
59
+ # Link OAuth to existing account
60
+ user = await user_service.link_oauth(user.id, "google", google_id)
61
+ else:
62
+ # Create new user
63
+ user = await user_service.create_oauth_user(
64
+ email=email,
65
+ full_name=full_name,
66
+ oauth_provider="google",
67
+ oauth_id=google_id,
68
+ )
69
+
70
+ access_token = create_access_token(subject=str(user.id))
71
+ refresh_token = create_refresh_token(subject=str(user.id))
72
+
73
+ # Redirect to frontend with tokens
74
+ params = urlencode({
75
+ "access_token": access_token,
76
+ "refresh_token": refresh_token,
77
+ })
78
+ return RedirectResponse(url=f"{FRONTEND_URL}/auth/callback?{params}")
79
+
80
+ except Exception as e:
81
+ params = urlencode({"error": str(e)})
82
+ return RedirectResponse(url=f"{FRONTEND_URL}/login?{params}")
83
+
84
+
85
+ {%- elif cookiecutter.use_mongodb %}
86
+
87
+
88
+ @router.get("/google/callback")
89
+ async def google_callback(request: Request, user_service: UserSvc):
90
+ """Handle Google OAuth2 callback.
91
+
92
+ Creates a new user if one doesn't exist with the Google email,
93
+ or returns tokens for existing user. Redirects to frontend with tokens.
94
+ """
95
+ try:
96
+ token = await oauth.google.authorize_access_token(request)
97
+ user_info = token.get("userinfo")
98
+
99
+ if not user_info:
100
+ params = urlencode({"error": "Failed to get user info from Google"})
101
+ return RedirectResponse(url=f"{FRONTEND_URL}/login?{params}")
102
+
103
+ email = user_info.get("email")
104
+ google_id = user_info.get("sub")
105
+ full_name = user_info.get("name")
106
+
107
+ # Try to find existing user by OAuth ID
108
+ user = await user_service.get_by_oauth("google", google_id)
109
+
110
+ if not user:
111
+ # Try to find by email (link existing account)
112
+ user = await user_service.get_by_email(email)
113
+ if user:
114
+ # Link OAuth to existing account
115
+ user = await user_service.link_oauth(str(user.id), "google", google_id)
116
+ else:
117
+ # Create new user
118
+ user = await user_service.create_oauth_user(
119
+ email=email,
120
+ full_name=full_name,
121
+ oauth_provider="google",
122
+ oauth_id=google_id,
123
+ )
124
+
125
+ access_token = create_access_token(subject=str(user.id))
126
+ refresh_token = create_refresh_token(subject=str(user.id))
127
+
128
+ # Redirect to frontend with tokens
129
+ params = urlencode({
130
+ "access_token": access_token,
131
+ "refresh_token": refresh_token,
132
+ })
133
+ return RedirectResponse(url=f"{FRONTEND_URL}/auth/callback?{params}")
134
+
135
+ except Exception as e:
136
+ params = urlencode({"error": str(e)})
137
+ return RedirectResponse(url=f"{FRONTEND_URL}/login?{params}")
138
+
139
+
140
+ {%- elif cookiecutter.use_sqlite %}
141
+
142
+
143
+ @router.get("/google/callback")
144
+ def google_callback(request: Request, user_service: UserSvc):
145
+ """Handle Google OAuth2 callback.
146
+
147
+ Creates a new user if one doesn't exist with the Google email,
148
+ or returns tokens for existing user. Redirects to frontend with tokens.
149
+ """
150
+ import asyncio
151
+
152
+ try:
153
+ # Run async OAuth in sync context
154
+ loop = asyncio.new_event_loop()
155
+ token = loop.run_until_complete(oauth.google.authorize_access_token(request))
156
+ loop.close()
157
+
158
+ user_info = token.get("userinfo")
159
+
160
+ if not user_info:
161
+ params = urlencode({"error": "Failed to get user info from Google"})
162
+ return RedirectResponse(url=f"{FRONTEND_URL}/login?{params}")
163
+
164
+ email = user_info.get("email")
165
+ google_id = user_info.get("sub")
166
+ full_name = user_info.get("name")
167
+
168
+ # Try to find existing user by OAuth ID
169
+ user = user_service.get_by_oauth("google", google_id)
170
+
171
+ if not user:
172
+ # Try to find by email (link existing account)
173
+ user = user_service.get_by_email(email)
174
+ if user:
175
+ # Link OAuth to existing account
176
+ user = user_service.link_oauth(user.id, "google", google_id)
177
+ else:
178
+ # Create new user
179
+ user = user_service.create_oauth_user(
180
+ email=email,
181
+ full_name=full_name,
182
+ oauth_provider="google",
183
+ oauth_id=google_id,
184
+ )
185
+
186
+ access_token = create_access_token(subject=user.id)
187
+ refresh_token = create_refresh_token(subject=user.id)
188
+
189
+ # Redirect to frontend with tokens
190
+ params = urlencode({
191
+ "access_token": access_token,
192
+ "refresh_token": refresh_token,
193
+ })
194
+ return RedirectResponse(url=f"{FRONTEND_URL}/auth/callback?{params}")
195
+
196
+ except Exception as e:
197
+ params = urlencode({"error": str(e)})
198
+ return RedirectResponse(url=f"{FRONTEND_URL}/login?{params}")
199
+
200
+
201
+ {%- endif %}
202
+ {%- endif %}
203
+ {%- else %}
204
+ """OAuth routes - not configured."""
205
+ {%- endif %}
@@ -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 %}