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.
Files changed (241) hide show
  1. fastapi_fullstack-0.1.7.dist-info/METADATA +739 -0
  2. fastapi_fullstack-0.1.7.dist-info/RECORD +241 -0
  3. fastapi_fullstack-0.1.7.dist-info/WHEEL +4 -0
  4. fastapi_fullstack-0.1.7.dist-info/entry_points.txt +2 -0
  5. fastapi_fullstack-0.1.7.dist-info/licenses/LICENSE +21 -0
  6. fastapi_gen/__init__.py +3 -0
  7. fastapi_gen/cli.py +442 -0
  8. fastapi_gen/config.py +356 -0
  9. fastapi_gen/generator.py +207 -0
  10. fastapi_gen/prompts.py +874 -0
  11. fastapi_gen/template/VARIABLES.md +276 -0
  12. fastapi_gen/template/cookiecutter.json +93 -0
  13. fastapi_gen/template/hooks/post_gen_project.py +355 -0
  14. fastapi_gen/template/{{cookiecutter.project_slug}}/.env.prod.example +56 -0
  15. fastapi_gen/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +150 -0
  16. fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore +109 -0
  17. fastapi_gen/template/{{cookiecutter.project_slug}}/AGENTS.md +55 -0
  18. fastapi_gen/template/{{cookiecutter.project_slug}}/CLAUDE.md +99 -0
  19. fastapi_gen/template/{{cookiecutter.project_slug}}/Makefile +315 -0
  20. fastapi_gen/template/{{cookiecutter.project_slug}}/README.md +768 -0
  21. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.dockerignore +60 -0
  22. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.env.example +155 -0
  23. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +32 -0
  24. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile +56 -0
  25. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +76 -0
  26. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +30 -0
  27. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
  28. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini +48 -0
  29. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +3 -0
  30. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +447 -0
  31. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +23 -0
  32. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +226 -0
  33. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/langchain_assistant.py +226 -0
  34. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/prompts.py +10 -0
  35. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +13 -0
  36. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +17 -0
  37. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +1 -0
  38. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +541 -0
  39. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +98 -0
  40. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +10 -0
  41. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +9 -0
  42. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +87 -0
  43. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +902 -0
  44. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +395 -0
  45. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +498 -0
  46. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +227 -0
  47. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +275 -0
  48. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +205 -0
  49. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +168 -0
  50. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +333 -0
  51. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +477 -0
  52. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +46 -0
  53. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +221 -0
  54. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +14 -0
  55. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +88 -0
  56. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +117 -0
  57. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +75 -0
  58. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +28 -0
  59. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +266 -0
  60. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +5 -0
  61. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +23 -0
  62. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +267 -0
  63. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +153 -0
  64. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +122 -0
  65. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +101 -0
  66. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +99 -0
  67. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +23 -0
  68. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +58 -0
  69. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +271 -0
  70. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +102 -0
  71. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +7 -0
  72. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +41 -0
  73. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +31 -0
  74. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +319 -0
  75. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +96 -0
  76. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +126 -0
  77. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +218 -0
  78. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +244 -0
  79. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +130 -0
  80. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +334 -0
  81. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +9 -0
  82. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +73 -0
  83. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +49 -0
  84. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +154 -0
  85. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +838 -0
  86. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +222 -0
  87. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +318 -0
  88. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +322 -0
  89. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +358 -0
  90. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +50 -0
  91. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +57 -0
  92. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +192 -0
  93. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +52 -0
  94. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +42 -0
  95. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +31 -0
  96. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +64 -0
  97. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +89 -0
  98. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +38 -0
  99. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +850 -0
  100. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +246 -0
  101. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +333 -0
  102. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +432 -0
  103. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +561 -0
  104. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +5 -0
  105. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +64 -0
  106. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +38 -0
  107. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +25 -0
  108. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +106 -0
  109. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +29 -0
  110. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +92 -0
  111. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +1 -0
  112. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +438 -0
  113. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +180 -0
  114. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
  115. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +1 -0
  116. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +1 -0
  117. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +242 -0
  118. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +151 -0
  119. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +113 -0
  120. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +310 -0
  121. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +253 -0
  122. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +151 -0
  123. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_admin.py +890 -0
  124. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +261 -0
  125. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +183 -0
  126. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +173 -0
  127. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +143 -0
  128. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +118 -0
  129. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +181 -0
  130. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +124 -0
  131. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +363 -0
  132. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +85 -0
  133. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +242 -0
  134. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +31 -0
  135. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +435 -0
  136. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.yml +241 -0
  137. fastapi_gen/template/{{cookiecutter.project_slug}}/docs/adding_features.md +132 -0
  138. fastapi_gen/template/{{cookiecutter.project_slug}}/docs/architecture.md +63 -0
  139. fastapi_gen/template/{{cookiecutter.project_slug}}/docs/patterns.md +161 -0
  140. fastapi_gen/template/{{cookiecutter.project_slug}}/docs/testing.md +120 -0
  141. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.dockerignore +40 -0
  142. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.env.example +12 -0
  143. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.gitignore +45 -0
  144. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +19 -0
  145. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +11 -0
  146. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +44 -0
  147. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/README.md +648 -0
  148. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +49 -0
  149. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +134 -0
  150. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +207 -0
  151. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +73 -0
  152. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +14 -0
  153. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +84 -0
  154. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +84 -0
  155. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +76 -0
  156. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/package.json +69 -0
  157. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +101 -0
  158. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +7 -0
  159. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/layout.tsx +11 -0
  160. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/login/page.tsx +5 -0
  161. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(auth)/register/page.tsx +5 -0
  162. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/chat/page.tsx +48 -0
  163. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/dashboard/page.tsx +99 -0
  164. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/layout.tsx +17 -0
  165. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/(dashboard)/profile/page.tsx +152 -0
  166. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/auth/callback/page.tsx +113 -0
  167. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/layout.tsx +46 -0
  168. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/[locale]/page.tsx +73 -0
  169. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +58 -0
  170. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +24 -0
  171. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +39 -0
  172. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +50 -0
  173. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +54 -0
  174. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +26 -0
  175. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +41 -0
  176. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +108 -0
  177. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +73 -0
  178. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +21 -0
  179. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +323 -0
  180. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +22 -0
  181. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +29 -0
  182. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +2 -0
  183. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +120 -0
  184. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +153 -0
  185. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +234 -0
  186. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +72 -0
  187. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +328 -0
  188. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/copy-button.tsx +46 -0
  189. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +11 -0
  190. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/local-conversation-sidebar.tsx +295 -0
  191. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/markdown-content.tsx +167 -0
  192. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +79 -0
  193. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +18 -0
  194. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +79 -0
  195. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +32 -0
  196. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +3 -0
  197. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +97 -0
  198. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +65 -0
  199. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +2 -0
  200. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +82 -0
  201. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +7 -0
  202. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +53 -0
  203. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +105 -0
  204. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +35 -0
  205. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +75 -0
  206. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +56 -0
  207. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +82 -0
  208. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +13 -0
  209. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +21 -0
  210. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +21 -0
  211. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/sheet.tsx +109 -0
  212. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +7 -0
  213. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +97 -0
  214. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +203 -0
  215. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +181 -0
  216. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-local-chat.ts +165 -0
  217. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +105 -0
  218. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +37 -0
  219. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +90 -0
  220. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +39 -0
  221. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +78 -0
  222. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +44 -0
  223. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +44 -0
  224. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +31 -0
  225. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +72 -0
  226. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +64 -0
  227. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-sidebar-store.ts +17 -0
  228. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +65 -0
  229. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +76 -0
  230. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +9 -0
  231. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/local-chat-store.ts +255 -0
  232. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/sidebar-store.ts +17 -0
  233. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +44 -0
  234. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +27 -0
  235. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +52 -0
  236. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +83 -0
  237. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +49 -0
  238. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +10 -0
  239. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +28 -0
  240. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +36 -0
  241. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +56 -0
@@ -0,0 +1,246 @@
1
+ {%- if cookiecutter.include_example_crud and cookiecutter.use_postgresql %}
2
+ """Item service (PostgreSQL async).
3
+
4
+ Contains business logic for item operations. Uses ItemRepository for database access.
5
+ """
6
+
7
+ from uuid import UUID
8
+
9
+ from sqlalchemy.ext.asyncio import AsyncSession
10
+
11
+ from app.core.exceptions import NotFoundError
12
+ from app.db.models.item import Item
13
+ from app.repositories import item_repo
14
+ from app.schemas.item import ItemCreate, ItemUpdate
15
+
16
+
17
+ class ItemService:
18
+ """Service for item-related business logic.
19
+
20
+ This is an example service demonstrating the service layer pattern.
21
+ Services contain business logic and use repositories for database operations.
22
+ """
23
+
24
+ def __init__(self, db: AsyncSession):
25
+ self.db = db
26
+
27
+ async def get_by_id(self, item_id: UUID) -> Item:
28
+ """Get item by ID.
29
+
30
+ Raises:
31
+ NotFoundError: If item does not exist.
32
+ """
33
+ item = await item_repo.get_by_id(self.db, item_id)
34
+ if not item:
35
+ raise NotFoundError(
36
+ message="Item not found",
37
+ details={"item_id": str(item_id)},
38
+ )
39
+ return item
40
+
41
+ async def get_multi(
42
+ self,
43
+ *,
44
+ skip: int = 0,
45
+ limit: int = 100,
46
+ active_only: bool = False,
47
+ ) -> list[Item]:
48
+ """Get multiple items with pagination."""
49
+ return await item_repo.get_multi(
50
+ self.db, skip=skip, limit=limit, active_only=active_only
51
+ )
52
+
53
+ async def create(self, item_in: ItemCreate) -> Item:
54
+ """Create a new item."""
55
+ return await item_repo.create(
56
+ self.db,
57
+ title=item_in.title,
58
+ description=item_in.description,
59
+ )
60
+
61
+ async def update(self, item_id: UUID, item_in: ItemUpdate) -> Item:
62
+ """Update an item.
63
+
64
+ Raises:
65
+ NotFoundError: If item does not exist.
66
+ """
67
+ item = await self.get_by_id(item_id)
68
+ update_data = item_in.model_dump(exclude_unset=True)
69
+ return await item_repo.update(self.db, db_item=item, update_data=update_data)
70
+
71
+ async def delete(self, item_id: UUID) -> Item:
72
+ """Delete an item.
73
+
74
+ Raises:
75
+ NotFoundError: If item does not exist.
76
+ """
77
+ item = await item_repo.delete(self.db, item_id)
78
+ if not item:
79
+ raise NotFoundError(
80
+ message="Item not found",
81
+ details={"item_id": str(item_id)},
82
+ )
83
+ return item
84
+
85
+
86
+ {%- elif cookiecutter.include_example_crud and cookiecutter.use_sqlite %}
87
+ """Item service (SQLite sync).
88
+
89
+ Contains business logic for item operations. Uses ItemRepository for database access.
90
+ """
91
+
92
+ from sqlalchemy.orm import Session
93
+
94
+ from app.core.exceptions import NotFoundError
95
+ from app.db.models.item import Item
96
+ from app.repositories import item_repo
97
+ from app.schemas.item import ItemCreate, ItemUpdate
98
+
99
+
100
+ class ItemService:
101
+ """Service for item-related business logic.
102
+
103
+ This is an example service demonstrating the service layer pattern.
104
+ Services contain business logic and use repositories for database operations.
105
+ """
106
+
107
+ def __init__(self, db: Session):
108
+ self.db = db
109
+
110
+ def get_by_id(self, item_id: str) -> Item:
111
+ """Get item by ID.
112
+
113
+ Raises:
114
+ NotFoundError: If item does not exist.
115
+ """
116
+ item = item_repo.get_by_id(self.db, item_id)
117
+ if not item:
118
+ raise NotFoundError(
119
+ message="Item not found",
120
+ details={"item_id": item_id},
121
+ )
122
+ return item
123
+
124
+ def get_multi(
125
+ self,
126
+ *,
127
+ skip: int = 0,
128
+ limit: int = 100,
129
+ active_only: bool = False,
130
+ ) -> list[Item]:
131
+ """Get multiple items with pagination."""
132
+ return item_repo.get_multi(
133
+ self.db, skip=skip, limit=limit, active_only=active_only
134
+ )
135
+
136
+ def create(self, item_in: ItemCreate) -> Item:
137
+ """Create a new item."""
138
+ return item_repo.create(
139
+ self.db,
140
+ title=item_in.title,
141
+ description=item_in.description,
142
+ )
143
+
144
+ def update(self, item_id: str, item_in: ItemUpdate) -> Item:
145
+ """Update an item.
146
+
147
+ Raises:
148
+ NotFoundError: If item does not exist.
149
+ """
150
+ item = self.get_by_id(item_id)
151
+ update_data = item_in.model_dump(exclude_unset=True)
152
+ return item_repo.update(self.db, db_item=item, update_data=update_data)
153
+
154
+ def delete(self, item_id: str) -> Item:
155
+ """Delete an item.
156
+
157
+ Raises:
158
+ NotFoundError: If item does not exist.
159
+ """
160
+ item = item_repo.delete(self.db, item_id)
161
+ if not item:
162
+ raise NotFoundError(
163
+ message="Item not found",
164
+ details={"item_id": item_id},
165
+ )
166
+ return item
167
+
168
+
169
+ {%- elif cookiecutter.include_example_crud and cookiecutter.use_mongodb %}
170
+ """Item service (MongoDB).
171
+
172
+ Contains business logic for item operations. Uses ItemRepository for database access.
173
+ """
174
+
175
+ from app.core.exceptions import NotFoundError
176
+ from app.db.models.item import Item
177
+ from app.repositories import item_repo
178
+ from app.schemas.item import ItemCreate, ItemUpdate
179
+
180
+
181
+ class ItemService:
182
+ """Service for item-related business logic.
183
+
184
+ This is an example service demonstrating the service layer pattern.
185
+ Services contain business logic and use repositories for database operations.
186
+ """
187
+
188
+ async def get_by_id(self, item_id: str) -> Item:
189
+ """Get item by ID.
190
+
191
+ Raises:
192
+ NotFoundError: If item does not exist.
193
+ """
194
+ item = await item_repo.get_by_id(item_id)
195
+ if not item:
196
+ raise NotFoundError(
197
+ message="Item not found",
198
+ details={"item_id": item_id},
199
+ )
200
+ return item
201
+
202
+ async def get_multi(
203
+ self,
204
+ *,
205
+ skip: int = 0,
206
+ limit: int = 100,
207
+ active_only: bool = False,
208
+ ) -> list[Item]:
209
+ """Get multiple items with pagination."""
210
+ return await item_repo.get_multi(skip=skip, limit=limit, active_only=active_only)
211
+
212
+ async def create(self, item_in: ItemCreate) -> Item:
213
+ """Create a new item."""
214
+ return await item_repo.create(
215
+ title=item_in.title,
216
+ description=item_in.description,
217
+ )
218
+
219
+ async def update(self, item_id: str, item_in: ItemUpdate) -> Item:
220
+ """Update an item.
221
+
222
+ Raises:
223
+ NotFoundError: If item does not exist.
224
+ """
225
+ item = await self.get_by_id(item_id)
226
+ update_data = item_in.model_dump(exclude_unset=True)
227
+ return await item_repo.update(db_item=item, update_data=update_data)
228
+
229
+ async def delete(self, item_id: str) -> Item:
230
+ """Delete an item.
231
+
232
+ Raises:
233
+ NotFoundError: If item does not exist.
234
+ """
235
+ item = await item_repo.delete(item_id)
236
+ if not item:
237
+ raise NotFoundError(
238
+ message="Item not found",
239
+ details={"item_id": item_id},
240
+ )
241
+ return item
242
+
243
+
244
+ {%- else %}
245
+ """Item service - not configured."""
246
+ {%- endif %}
@@ -0,0 +1,333 @@
1
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
2
+ {%- if cookiecutter.use_postgresql %}
3
+ """Session service (PostgreSQL async)."""
4
+
5
+ import hashlib
6
+ from datetime import datetime, timedelta
7
+ from uuid import UUID
8
+
9
+ from sqlalchemy.ext.asyncio import AsyncSession
10
+
11
+ from app.core.config import settings
12
+ from app.core.exceptions import NotFoundError
13
+ from app.db.models.session import Session
14
+ from app.repositories import session_repo
15
+
16
+
17
+ def _hash_token(token: str) -> str:
18
+ """Hash a token for storage."""
19
+ return hashlib.sha256(token.encode()).hexdigest()
20
+
21
+
22
+ def _parse_user_agent(user_agent: str | None) -> tuple[str | None, str | None]:
23
+ """Parse user agent to extract device name and type."""
24
+ if not user_agent:
25
+ return None, None
26
+
27
+ user_agent_lower = user_agent.lower()
28
+
29
+ # Detect device type
30
+ if "mobile" in user_agent_lower or "android" in user_agent_lower:
31
+ device_type = "mobile"
32
+ elif "tablet" in user_agent_lower or "ipad" in user_agent_lower:
33
+ device_type = "tablet"
34
+ else:
35
+ device_type = "desktop"
36
+
37
+ # Extract browser/device name
38
+ if "chrome" in user_agent_lower:
39
+ device_name = "Chrome"
40
+ elif "firefox" in user_agent_lower:
41
+ device_name = "Firefox"
42
+ elif "safari" in user_agent_lower:
43
+ device_name = "Safari"
44
+ elif "edge" in user_agent_lower:
45
+ device_name = "Edge"
46
+ else:
47
+ device_name = "Unknown Browser"
48
+
49
+ return device_name, device_type
50
+
51
+
52
+ class SessionService:
53
+ """Service for session management."""
54
+
55
+ def __init__(self, db: AsyncSession):
56
+ self.db = db
57
+
58
+ async def create_session(
59
+ self,
60
+ user_id: UUID,
61
+ refresh_token: str,
62
+ ip_address: str | None = None,
63
+ user_agent: str | None = None,
64
+ ) -> Session:
65
+ """Create a new session for a user."""
66
+ device_name, device_type = _parse_user_agent(user_agent)
67
+ expires_at = datetime.utcnow() + timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
68
+
69
+ return await session_repo.create(
70
+ self.db,
71
+ user_id=user_id,
72
+ refresh_token_hash=_hash_token(refresh_token),
73
+ expires_at=expires_at,
74
+ device_name=device_name,
75
+ device_type=device_type,
76
+ ip_address=ip_address,
77
+ user_agent=user_agent,
78
+ )
79
+
80
+ async def get_user_sessions(self, user_id: UUID) -> list[Session]:
81
+ """Get all active sessions for a user."""
82
+ return await session_repo.get_user_sessions(self.db, user_id, active_only=True)
83
+
84
+ async def validate_refresh_token(self, refresh_token: str) -> Session | None:
85
+ """Validate a refresh token and return the session if valid."""
86
+ token_hash = _hash_token(refresh_token)
87
+ session = await session_repo.get_by_refresh_token_hash(self.db, token_hash)
88
+
89
+ if session and session.expires_at > datetime.utcnow():
90
+ await session_repo.update_last_used(self.db, session.id)
91
+ return session
92
+
93
+ return None
94
+
95
+ async def logout_session(self, session_id: UUID, user_id: UUID) -> Session:
96
+ """Logout a specific session."""
97
+ session = await session_repo.get_by_id(self.db, session_id)
98
+ if not session or session.user_id != user_id:
99
+ raise NotFoundError(message="Session not found")
100
+
101
+ await session_repo.deactivate(self.db, session_id)
102
+ return session
103
+
104
+ async def logout_all_sessions(self, user_id: UUID) -> int:
105
+ """Logout all sessions for a user. Returns count of logged out sessions."""
106
+ return await session_repo.deactivate_all_user_sessions(self.db, user_id)
107
+
108
+ async def logout_by_refresh_token(self, refresh_token: str) -> Session | None:
109
+ """Logout session by refresh token."""
110
+ token_hash = _hash_token(refresh_token)
111
+ return await session_repo.deactivate_by_refresh_token_hash(self.db, token_hash)
112
+
113
+
114
+ {%- elif cookiecutter.use_sqlite %}
115
+ """Session service (SQLite sync)."""
116
+
117
+ import hashlib
118
+ from datetime import datetime, timedelta
119
+
120
+ from sqlalchemy.orm import Session as DBSession
121
+
122
+ from app.core.config import settings
123
+ from app.core.exceptions import NotFoundError
124
+ from app.db.models.session import Session
125
+ from app.repositories import session_repo
126
+
127
+
128
+ def _hash_token(token: str) -> str:
129
+ """Hash a token for storage."""
130
+ return hashlib.sha256(token.encode()).hexdigest()
131
+
132
+
133
+ def _parse_user_agent(user_agent: str | None) -> tuple[str | None, str | None]:
134
+ """Parse user agent to extract device name and type."""
135
+ if not user_agent:
136
+ return None, None
137
+
138
+ user_agent_lower = user_agent.lower()
139
+
140
+ # Detect device type
141
+ if "mobile" in user_agent_lower or "android" in user_agent_lower:
142
+ device_type = "mobile"
143
+ elif "tablet" in user_agent_lower or "ipad" in user_agent_lower:
144
+ device_type = "tablet"
145
+ else:
146
+ device_type = "desktop"
147
+
148
+ # Extract browser/device name
149
+ if "chrome" in user_agent_lower:
150
+ device_name = "Chrome"
151
+ elif "firefox" in user_agent_lower:
152
+ device_name = "Firefox"
153
+ elif "safari" in user_agent_lower:
154
+ device_name = "Safari"
155
+ elif "edge" in user_agent_lower:
156
+ device_name = "Edge"
157
+ else:
158
+ device_name = "Unknown Browser"
159
+
160
+ return device_name, device_type
161
+
162
+
163
+ class SessionService:
164
+ """Service for session management."""
165
+
166
+ def __init__(self, db: DBSession):
167
+ self.db = db
168
+
169
+ def create_session(
170
+ self,
171
+ user_id: str,
172
+ refresh_token: str,
173
+ ip_address: str | None = None,
174
+ user_agent: str | None = None,
175
+ ) -> Session:
176
+ """Create a new session for a user."""
177
+ device_name, device_type = _parse_user_agent(user_agent)
178
+ expires_at = datetime.utcnow() + timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
179
+
180
+ return session_repo.create(
181
+ self.db,
182
+ user_id=user_id,
183
+ refresh_token_hash=_hash_token(refresh_token),
184
+ expires_at=expires_at,
185
+ device_name=device_name,
186
+ device_type=device_type,
187
+ ip_address=ip_address,
188
+ user_agent=user_agent,
189
+ )
190
+
191
+ def get_user_sessions(self, user_id: str) -> list[Session]:
192
+ """Get all active sessions for a user."""
193
+ return session_repo.get_user_sessions(self.db, user_id, active_only=True)
194
+
195
+ def validate_refresh_token(self, refresh_token: str) -> Session | None:
196
+ """Validate a refresh token and return the session if valid."""
197
+ token_hash = _hash_token(refresh_token)
198
+ session = session_repo.get_by_refresh_token_hash(self.db, token_hash)
199
+
200
+ if session and session.expires_at > datetime.utcnow():
201
+ session_repo.update_last_used(self.db, session.id)
202
+ return session
203
+
204
+ return None
205
+
206
+ def logout_session(self, session_id: str, user_id: str) -> Session:
207
+ """Logout a specific session."""
208
+ session = session_repo.get_by_id(self.db, session_id)
209
+ if not session or session.user_id != user_id:
210
+ raise NotFoundError(message="Session not found")
211
+
212
+ session_repo.deactivate(self.db, session_id)
213
+ return session
214
+
215
+ def logout_all_sessions(self, user_id: str) -> int:
216
+ """Logout all sessions for a user. Returns count of logged out sessions."""
217
+ return session_repo.deactivate_all_user_sessions(self.db, user_id)
218
+
219
+ def logout_by_refresh_token(self, refresh_token: str) -> Session | None:
220
+ """Logout session by refresh token."""
221
+ token_hash = _hash_token(refresh_token)
222
+ return session_repo.deactivate_by_refresh_token_hash(self.db, token_hash)
223
+
224
+
225
+ {%- elif cookiecutter.use_mongodb %}
226
+ """Session service (MongoDB)."""
227
+
228
+ import hashlib
229
+ from datetime import UTC, datetime, timedelta
230
+
231
+ from app.core.config import settings
232
+ from app.core.exceptions import NotFoundError
233
+ from app.db.models.session import Session
234
+ from app.repositories import session_repo
235
+
236
+
237
+ def _hash_token(token: str) -> str:
238
+ """Hash a token for storage."""
239
+ return hashlib.sha256(token.encode()).hexdigest()
240
+
241
+
242
+ def _parse_user_agent(user_agent: str | None) -> tuple[str | None, str | None]:
243
+ """Parse user agent to extract device name and type."""
244
+ if not user_agent:
245
+ return None, None
246
+
247
+ user_agent_lower = user_agent.lower()
248
+
249
+ # Detect device type
250
+ if "mobile" in user_agent_lower or "android" in user_agent_lower:
251
+ device_type = "mobile"
252
+ elif "tablet" in user_agent_lower or "ipad" in user_agent_lower:
253
+ device_type = "tablet"
254
+ else:
255
+ device_type = "desktop"
256
+
257
+ # Extract browser/device name
258
+ if "chrome" in user_agent_lower:
259
+ device_name = "Chrome"
260
+ elif "firefox" in user_agent_lower:
261
+ device_name = "Firefox"
262
+ elif "safari" in user_agent_lower:
263
+ device_name = "Safari"
264
+ elif "edge" in user_agent_lower:
265
+ device_name = "Edge"
266
+ else:
267
+ device_name = "Unknown Browser"
268
+
269
+ return device_name, device_type
270
+
271
+
272
+ class SessionService:
273
+ """Service for session management."""
274
+
275
+ async def create_session(
276
+ self,
277
+ user_id: str,
278
+ refresh_token: str,
279
+ ip_address: str | None = None,
280
+ user_agent: str | None = None,
281
+ ) -> Session:
282
+ """Create a new session for a user."""
283
+ device_name, device_type = _parse_user_agent(user_agent)
284
+ expires_at = datetime.now(UTC) + timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
285
+
286
+ return await session_repo.create(
287
+ user_id=user_id,
288
+ refresh_token_hash=_hash_token(refresh_token),
289
+ expires_at=expires_at,
290
+ device_name=device_name,
291
+ device_type=device_type,
292
+ ip_address=ip_address,
293
+ user_agent=user_agent,
294
+ )
295
+
296
+ async def get_user_sessions(self, user_id: str) -> list[Session]:
297
+ """Get all active sessions for a user."""
298
+ return await session_repo.get_user_sessions(user_id, active_only=True)
299
+
300
+ async def validate_refresh_token(self, refresh_token: str) -> Session | None:
301
+ """Validate a refresh token and return the session if valid."""
302
+ token_hash = _hash_token(refresh_token)
303
+ session = await session_repo.get_by_refresh_token_hash(token_hash)
304
+
305
+ if session and session.expires_at > datetime.now(UTC):
306
+ await session_repo.update_last_used(str(session.id))
307
+ return session
308
+
309
+ return None
310
+
311
+ async def logout_session(self, session_id: str, user_id: str) -> Session:
312
+ """Logout a specific session."""
313
+ session = await session_repo.get_by_id(session_id)
314
+ if not session or session.user_id != user_id:
315
+ raise NotFoundError(message="Session not found")
316
+
317
+ await session_repo.deactivate(session_id)
318
+ return session
319
+
320
+ async def logout_all_sessions(self, user_id: str) -> int:
321
+ """Logout all sessions for a user. Returns count of logged out sessions."""
322
+ return await session_repo.deactivate_all_user_sessions(user_id)
323
+
324
+ async def logout_by_refresh_token(self, refresh_token: str) -> Session | None:
325
+ """Logout session by refresh token."""
326
+ token_hash = _hash_token(refresh_token)
327
+ return await session_repo.deactivate_by_refresh_token_hash(token_hash)
328
+
329
+
330
+ {%- endif %}
331
+ {%- else %}
332
+ """Session service - not configured."""
333
+ {%- endif %}