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,165 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { nanoid } from "nanoid";
5
+ import { useWebSocket } from "./use-websocket";
6
+ import { useLocalChatStore } from "@/stores/local-chat-store";
7
+ import type { ChatMessage, ToolCall, WSEvent } from "@/types";
8
+ import { WS_URL } from "@/lib/constants";
9
+
10
+ export function useLocalChat() {
11
+ const {
12
+ currentConversationId,
13
+ getCurrentMessages,
14
+ createConversation,
15
+ addMessage,
16
+ updateMessage,
17
+ addToolCall,
18
+ updateToolCall,
19
+ clearCurrentMessages,
20
+ } = useLocalChatStore();
21
+
22
+ const messages = getCurrentMessages();
23
+ const [isProcessing, setIsProcessing] = useState(false);
24
+ const [currentMessageId, setCurrentMessageId] = useState<string | null>(null);
25
+
26
+ const handleWebSocketMessage = useCallback(
27
+ (event: MessageEvent) => {
28
+ const wsEvent: WSEvent = JSON.parse(event.data);
29
+
30
+ switch (wsEvent.type) {
31
+ case "model_request_start": {
32
+ const newMsgId = nanoid();
33
+ setCurrentMessageId(newMsgId);
34
+ addMessage({
35
+ id: newMsgId,
36
+ role: "assistant",
37
+ content: "",
38
+ timestamp: new Date(),
39
+ isStreaming: true,
40
+ toolCalls: [],
41
+ });
42
+ break;
43
+ }
44
+
45
+ case "text_delta": {
46
+ if (currentMessageId) {
47
+ const content = (wsEvent.data as { index: number; content: string })
48
+ .content;
49
+ updateMessage(currentMessageId, (msg) => ({
50
+ ...msg,
51
+ content: msg.content + content,
52
+ }));
53
+ }
54
+ break;
55
+ }
56
+
57
+ case "tool_call": {
58
+ if (currentMessageId) {
59
+ const { tool_name, args, tool_call_id } = wsEvent.data as {
60
+ tool_name: string;
61
+ args: Record<string, unknown>;
62
+ tool_call_id: string;
63
+ };
64
+ const toolCall: ToolCall = {
65
+ id: tool_call_id,
66
+ name: tool_name,
67
+ args,
68
+ status: "running",
69
+ };
70
+ addToolCall(currentMessageId, toolCall);
71
+ }
72
+ break;
73
+ }
74
+
75
+ case "tool_result": {
76
+ if (currentMessageId) {
77
+ const { tool_call_id, content } = wsEvent.data as {
78
+ tool_call_id: string;
79
+ content: string;
80
+ };
81
+ updateToolCall(currentMessageId, tool_call_id, {
82
+ result: content,
83
+ status: "completed",
84
+ });
85
+ }
86
+ break;
87
+ }
88
+
89
+ case "final_result": {
90
+ if (currentMessageId) {
91
+ updateMessage(currentMessageId, (msg) => ({
92
+ ...msg,
93
+ isStreaming: false,
94
+ }));
95
+ }
96
+ setIsProcessing(false);
97
+ setCurrentMessageId(null);
98
+ break;
99
+ }
100
+
101
+ case "error": {
102
+ if (currentMessageId) {
103
+ updateMessage(currentMessageId, (msg) => ({
104
+ ...msg,
105
+ content: msg.content + "\n\n[Error occurred]",
106
+ isStreaming: false,
107
+ }));
108
+ }
109
+ setIsProcessing(false);
110
+ break;
111
+ }
112
+
113
+ case "complete": {
114
+ setIsProcessing(false);
115
+ break;
116
+ }
117
+ }
118
+ },
119
+ [currentMessageId, addMessage, updateMessage, addToolCall, updateToolCall]
120
+ );
121
+
122
+ const wsUrl = `${WS_URL}/api/v1/ws/agent`;
123
+
124
+ const { isConnected, connect, disconnect, sendMessage } = useWebSocket({
125
+ url: wsUrl,
126
+ onMessage: handleWebSocketMessage,
127
+ });
128
+
129
+ const sendChatMessage = useCallback(
130
+ (content: string) => {
131
+ let convId = currentConversationId;
132
+ if (!convId) {
133
+ convId = createConversation();
134
+ }
135
+
136
+ const userMessage: ChatMessage = {
137
+ id: nanoid(),
138
+ role: "user",
139
+ content,
140
+ timestamp: new Date(),
141
+ };
142
+ addMessage(userMessage);
143
+
144
+ setIsProcessing(true);
145
+ sendMessage({ message: content });
146
+ },
147
+ [addMessage, sendMessage, currentConversationId, createConversation]
148
+ );
149
+
150
+ const startNewChat = useCallback(() => {
151
+ createConversation();
152
+ }, [createConversation]);
153
+
154
+ return {
155
+ messages,
156
+ currentConversationId,
157
+ isConnected,
158
+ isProcessing,
159
+ connect,
160
+ disconnect,
161
+ sendMessage: sendChatMessage,
162
+ clearMessages: clearCurrentMessages,
163
+ startNewChat,
164
+ };
165
+ }
@@ -0,0 +1,105 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+
5
+ interface UseWebSocketOptions {
6
+ url: string;
7
+ onMessage?: (event: MessageEvent) => void;
8
+ onOpen?: () => void;
9
+ onClose?: () => void;
10
+ onError?: (error: Event) => void;
11
+ reconnect?: boolean;
12
+ reconnectInterval?: number;
13
+ maxReconnectAttempts?: number;
14
+ }
15
+
16
+ export function useWebSocket({
17
+ url,
18
+ onMessage,
19
+ onOpen,
20
+ onClose,
21
+ onError,
22
+ reconnect = true,
23
+ reconnectInterval = 3000,
24
+ maxReconnectAttempts = 5,
25
+ }: UseWebSocketOptions) {
26
+ const [isConnected, setIsConnected] = useState(false);
27
+ const wsRef = useRef<WebSocket | null>(null);
28
+ const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
29
+ const reconnectAttemptsRef = useRef(0);
30
+
31
+ // Use refs for callbacks to avoid recreating connect function
32
+ const onMessageRef = useRef(onMessage);
33
+ const onOpenRef = useRef(onOpen);
34
+ const onCloseRef = useRef(onClose);
35
+ const onErrorRef = useRef(onError);
36
+
37
+ // Keep refs updated
38
+ useEffect(() => {
39
+ onMessageRef.current = onMessage;
40
+ onOpenRef.current = onOpen;
41
+ onCloseRef.current = onClose;
42
+ onErrorRef.current = onError;
43
+ }, [onMessage, onOpen, onClose, onError]);
44
+
45
+ const connect = useCallback(() => {
46
+ if (wsRef.current?.readyState === WebSocket.OPEN) return;
47
+
48
+ const ws = new WebSocket(url);
49
+ wsRef.current = ws;
50
+
51
+ ws.onopen = () => {
52
+ setIsConnected(true);
53
+ reconnectAttemptsRef.current = 0;
54
+ onOpenRef.current?.();
55
+ };
56
+
57
+ ws.onmessage = (event) => {
58
+ onMessageRef.current?.(event);
59
+ };
60
+
61
+ ws.onclose = () => {
62
+ setIsConnected(false);
63
+ onCloseRef.current?.();
64
+
65
+ if (reconnect && reconnectAttemptsRef.current < maxReconnectAttempts) {
66
+ reconnectTimeoutRef.current = setTimeout(() => {
67
+ reconnectAttemptsRef.current += 1;
68
+ connect();
69
+ }, reconnectInterval);
70
+ }
71
+ };
72
+
73
+ ws.onerror = (error) => {
74
+ onErrorRef.current?.(error);
75
+ };
76
+ }, [url, reconnect, reconnectInterval, maxReconnectAttempts]);
77
+
78
+ const disconnect = useCallback(() => {
79
+ if (reconnectTimeoutRef.current) {
80
+ clearTimeout(reconnectTimeoutRef.current);
81
+ }
82
+ wsRef.current?.close();
83
+ wsRef.current = null;
84
+ }, []);
85
+
86
+ const sendMessage = useCallback((data: string | object) => {
87
+ if (wsRef.current?.readyState === WebSocket.OPEN) {
88
+ const message = typeof data === "string" ? data : JSON.stringify(data);
89
+ wsRef.current.send(message);
90
+ }
91
+ }, []);
92
+
93
+ useEffect(() => {
94
+ return () => {
95
+ disconnect();
96
+ };
97
+ }, [disconnect]);
98
+
99
+ return {
100
+ isConnected,
101
+ connect,
102
+ disconnect,
103
+ sendMessage,
104
+ };
105
+ }
@@ -0,0 +1,37 @@
1
+ {%- if cookiecutter.enable_i18n %}
2
+ import { getRequestConfig } from "next-intl/server";
3
+
4
+ // Supported locales
5
+ export const locales = ["en", "pl"] as const;
6
+ export type Locale = (typeof locales)[number];
7
+
8
+ export const defaultLocale: Locale = "en";
9
+
10
+ export default getRequestConfig(async ({ requestLocale }) => {
11
+ // This typically corresponds to the `[locale]` segment
12
+ let locale = await requestLocale;
13
+
14
+ // Ensure that a valid locale is used
15
+ if (!locale || !locales.includes(locale as Locale)) {
16
+ locale = defaultLocale;
17
+ }
18
+
19
+ return {
20
+ locale,
21
+ messages: (await import(`../messages/${locale}.json`)).default,
22
+ };
23
+ });
24
+
25
+ export function getLocaleLabel(locale: Locale): string {
26
+ const labels: Record<Locale, string> = {
27
+ en: "English",
28
+ pl: "Polski",
29
+ };
30
+ return labels[locale];
31
+ }
32
+ {%- else %}
33
+ // i18n is disabled
34
+ export const locales = ["en"] as const;
35
+ export type Locale = (typeof locales)[number];
36
+ export const defaultLocale: Locale = "en";
37
+ {%- endif %}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Client-side API client.
3
+ * All requests go through Next.js API routes (/api/*), never directly to the backend.
4
+ * This keeps the backend URL hidden from the browser.
5
+ */
6
+
7
+ export class ApiError extends Error {
8
+ constructor(
9
+ public status: number,
10
+ public message: string,
11
+ public data?: unknown
12
+ ) {
13
+ super(message);
14
+ this.name = "ApiError";
15
+ }
16
+ }
17
+
18
+ interface RequestOptions extends Omit<RequestInit, "body"> {
19
+ params?: Record<string, string>;
20
+ body?: unknown;
21
+ }
22
+
23
+ class ApiClient {
24
+ private async request<T>(
25
+ endpoint: string,
26
+ options: RequestOptions = {}
27
+ ): Promise<T> {
28
+ const { params, body, ...fetchOptions } = options;
29
+
30
+ let url = `/api${endpoint}`;
31
+
32
+ if (params) {
33
+ const searchParams = new URLSearchParams(params);
34
+ url += `?${searchParams.toString()}`;
35
+ }
36
+
37
+ const response = await fetch(url, {
38
+ ...fetchOptions,
39
+ headers: {
40
+ "Content-Type": "application/json",
41
+ ...fetchOptions.headers,
42
+ },
43
+ body: body ? JSON.stringify(body) : undefined,
44
+ });
45
+
46
+ if (!response.ok) {
47
+ let errorData;
48
+ try {
49
+ errorData = await response.json();
50
+ } catch {
51
+ errorData = null;
52
+ }
53
+ throw new ApiError(
54
+ response.status,
55
+ errorData?.detail || errorData?.message || "Request failed",
56
+ errorData
57
+ );
58
+ }
59
+
60
+ // Handle empty responses
61
+ const text = await response.text();
62
+ if (!text) {
63
+ return null as T;
64
+ }
65
+
66
+ return JSON.parse(text);
67
+ }
68
+
69
+ get<T>(endpoint: string, options?: RequestOptions) {
70
+ return this.request<T>(endpoint, { ...options, method: "GET" });
71
+ }
72
+
73
+ post<T>(endpoint: string, body?: unknown, options?: RequestOptions) {
74
+ return this.request<T>(endpoint, { ...options, method: "POST", body });
75
+ }
76
+
77
+ put<T>(endpoint: string, body?: unknown, options?: RequestOptions) {
78
+ return this.request<T>(endpoint, { ...options, method: "PUT", body });
79
+ }
80
+
81
+ patch<T>(endpoint: string, body?: unknown, options?: RequestOptions) {
82
+ return this.request<T>(endpoint, { ...options, method: "PATCH", body });
83
+ }
84
+
85
+ delete<T>(endpoint: string, options?: RequestOptions) {
86
+ return this.request<T>(endpoint, { ...options, method: "DELETE" });
87
+ }
88
+ }
89
+
90
+ export const apiClient = new ApiClient();
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Application constants.
3
+ */
4
+
5
+ export const APP_NAME = "{{ cookiecutter.project_name }}";
6
+ export const APP_DESCRIPTION = "{{ cookiecutter.project_description }}";
7
+
8
+ // API Routes (Next.js internal routes)
9
+ export const API_ROUTES = {
10
+ // Auth
11
+ LOGIN: "/auth/login",
12
+ REGISTER: "/auth/register",
13
+ LOGOUT: "/auth/logout",
14
+ REFRESH: "/auth/refresh",
15
+ ME: "/auth/me",
16
+
17
+ // Health
18
+ HEALTH: "/health",
19
+
20
+ // Users
21
+ USERS: "/users",
22
+
23
+ // Chat (AI Agent)
24
+ CHAT: "/chat",
25
+ } as const;
26
+
27
+ // Navigation routes
28
+ export const ROUTES = {
29
+ HOME: "/",
30
+ LOGIN: "/login",
31
+ REGISTER: "/register",
32
+ DASHBOARD: "/dashboard",
33
+ CHAT: "/chat",
34
+ PROFILE: "/profile",
35
+ SETTINGS: "/settings",
36
+ } as const;
37
+
38
+ // WebSocket URL (for chat - this needs to be direct to backend for WS)
39
+ export const WS_URL = process.env.NEXT_PUBLIC_WS_URL || "ws://localhost:8000";
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Server-side API client for calling the FastAPI backend.
3
+ * This module is used by Next.js API routes to proxy requests.
4
+ * IMPORTANT: This file should only be imported in server-side code (API routes, Server Components).
5
+ */
6
+
7
+ const BACKEND_URL = process.env.BACKEND_URL || "http://localhost:8000";
8
+
9
+ export class BackendApiError extends Error {
10
+ constructor(
11
+ public status: number,
12
+ public statusText: string,
13
+ public data?: unknown
14
+ ) {
15
+ super(`Backend API error: ${status} ${statusText}`);
16
+ this.name = "BackendApiError";
17
+ }
18
+ }
19
+
20
+ interface RequestOptions extends RequestInit {
21
+ params?: Record<string, string>;
22
+ }
23
+
24
+ /**
25
+ * Make a request to the FastAPI backend.
26
+ * This should only be called from Next.js API routes or Server Components.
27
+ */
28
+ export async function backendFetch<T>(
29
+ endpoint: string,
30
+ options: RequestOptions = {}
31
+ ): Promise<T> {
32
+ const { params, ...fetchOptions } = options;
33
+
34
+ let url = `${BACKEND_URL}${endpoint}`;
35
+
36
+ if (params) {
37
+ const searchParams = new URLSearchParams(params);
38
+ url += `?${searchParams.toString()}`;
39
+ }
40
+
41
+ const response = await fetch(url, {
42
+ ...fetchOptions,
43
+ headers: {
44
+ "Content-Type": "application/json",
45
+ ...fetchOptions.headers,
46
+ },
47
+ });
48
+
49
+ if (!response.ok) {
50
+ let errorData;
51
+ try {
52
+ errorData = await response.json();
53
+ } catch {
54
+ errorData = null;
55
+ }
56
+ throw new BackendApiError(response.status, response.statusText, errorData);
57
+ }
58
+
59
+ // Handle empty responses
60
+ const text = await response.text();
61
+ if (!text) {
62
+ return null as T;
63
+ }
64
+
65
+ return JSON.parse(text);
66
+ }
67
+
68
+ /**
69
+ * Forward authorization header from the incoming request to the backend.
70
+ */
71
+ export function getAuthHeaders(
72
+ authHeader: string | null
73
+ ): Record<string, string> {
74
+ if (!authHeader) {
75
+ return {};
76
+ }
77
+ return { Authorization: authHeader };
78
+ }
@@ -0,0 +1,44 @@
1
+ {%- if cookiecutter.use_frontend %}
2
+ import { describe, it, expect } from "vitest";
3
+ import { cn } from "./utils";
4
+
5
+ describe("cn utility function", () => {
6
+ it("should merge class names", () => {
7
+ const result = cn("class1", "class2");
8
+ expect(result).toBe("class1 class2");
9
+ });
10
+
11
+ it("should handle conditional classes", () => {
12
+ const result = cn("base", { active: true, disabled: false });
13
+ expect(result).toContain("base");
14
+ expect(result).toContain("active");
15
+ expect(result).not.toContain("disabled");
16
+ });
17
+
18
+ it("should handle undefined and null values", () => {
19
+ const result = cn("base", undefined, null, "extra");
20
+ expect(result).toBe("base extra");
21
+ });
22
+
23
+ it("should merge tailwind classes correctly", () => {
24
+ // tailwind-merge should handle conflicting utilities
25
+ const result = cn("px-2 py-1", "px-4");
26
+ expect(result).toContain("px-4");
27
+ expect(result).toContain("py-1");
28
+ });
29
+
30
+ it("should handle empty input", () => {
31
+ const result = cn();
32
+ expect(result).toBe("");
33
+ });
34
+
35
+ it("should handle array of classes", () => {
36
+ const result = cn(["class1", "class2"]);
37
+ expect(result).toContain("class1");
38
+ expect(result).toContain("class2");
39
+ });
40
+ });
41
+ {%- else %}
42
+ /* Utils tests - frontend not configured */
43
+ export {};
44
+ {%- endif %}
@@ -0,0 +1,44 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ /**
5
+ * Merge Tailwind CSS classes with clsx.
6
+ * This is the standard utility used by shadcn/ui components.
7
+ */
8
+ export function cn(...inputs: ClassValue[]) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+
12
+ /**
13
+ * Format a date to a human-readable string.
14
+ */
15
+ export function formatDate(date: Date | string): string {
16
+ const d = typeof date === "string" ? new Date(date) : date;
17
+ return d.toLocaleDateString("en-US", {
18
+ year: "numeric",
19
+ month: "short",
20
+ day: "numeric",
21
+ });
22
+ }
23
+
24
+ /**
25
+ * Format a date to include time.
26
+ */
27
+ export function formatDateTime(date: Date | string): string {
28
+ const d = typeof date === "string" ? new Date(date) : date;
29
+ return d.toLocaleString("en-US", {
30
+ year: "numeric",
31
+ month: "short",
32
+ day: "numeric",
33
+ hour: "2-digit",
34
+ minute: "2-digit",
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Truncate a string to a maximum length.
40
+ */
41
+ export function truncate(str: string, maxLength: number): string {
42
+ if (str.length <= maxLength) return str;
43
+ return str.slice(0, maxLength - 3) + "...";
44
+ }
@@ -0,0 +1,31 @@
1
+ {%- if cookiecutter.enable_i18n %}
2
+ import createMiddleware from "next-intl/middleware";
3
+ import { locales, defaultLocale } from "./i18n";
4
+
5
+ export default createMiddleware({
6
+ // A list of all locales that are supported
7
+ locales,
8
+
9
+ // Used when no locale matches
10
+ defaultLocale,
11
+
12
+ // Don't prefix the default locale (e.g., /about instead of /en/about)
13
+ localePrefix: "as-needed",
14
+ });
15
+
16
+ export const config = {
17
+ // Match only internationalized pathnames
18
+ matcher: [
19
+ // Match all pathnames except for:
20
+ // - /api (API routes)
21
+ // - /_next (Next.js internals)
22
+ // - /static (inside /public)
23
+ // - /_vercel (Vercel internals)
24
+ // - All root files like favicon.ico, robots.txt, etc.
25
+ "/((?!api|_next|_vercel|static|.*\\..*).*)",
26
+ ],
27
+ };
28
+ {%- else %}
29
+ // Middleware is disabled when i18n is not enabled
30
+ export {};
31
+ {%- endif %}