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,203 @@
1
+ "use client";
2
+
3
+ import { useCallback, useState } from "react";
4
+ import { nanoid } from "nanoid";
5
+ import { useWebSocket } from "./use-websocket";
6
+ import { useChatStore } from "@/stores";
7
+ import type { ChatMessage, ToolCall, WSEvent } from "@/types";
8
+ import { WS_URL } from "@/lib/constants";
9
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
10
+ import { useConversationStore } from "@/stores";
11
+ {%- endif %}
12
+
13
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
14
+ interface UseChatOptions {
15
+ conversationId?: string | null;
16
+ onConversationCreated?: (conversationId: string) => void;
17
+ }
18
+
19
+ export function useChat(options: UseChatOptions = {}) {
20
+ const { conversationId, onConversationCreated } = options;
21
+ const { setCurrentConversationId } = useConversationStore();
22
+ {%- else %}
23
+ export function useChat() {
24
+ {%- endif %}
25
+ const {
26
+ messages,
27
+ addMessage,
28
+ updateMessage,
29
+ addToolCall,
30
+ updateToolCall,
31
+ clearMessages,
32
+ } = useChatStore();
33
+
34
+ const [isProcessing, setIsProcessing] = useState(false);
35
+ const [currentMessageId, setCurrentMessageId] = useState<string | null>(null);
36
+
37
+ const handleWebSocketMessage = useCallback(
38
+ (event: MessageEvent) => {
39
+ const wsEvent: WSEvent = JSON.parse(event.data);
40
+
41
+ switch (wsEvent.type) {
42
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
43
+ case "conversation_created": {
44
+ // Handle new conversation created by backend
45
+ const { conversation_id } = wsEvent.data as { conversation_id: string };
46
+ setCurrentConversationId(conversation_id);
47
+ onConversationCreated?.(conversation_id);
48
+ break;
49
+ }
50
+
51
+ case "message_saved": {
52
+ // Message was saved to database, update local ID if needed
53
+ // We don't need to do anything special here for now
54
+ break;
55
+ }
56
+ {%- endif %}
57
+
58
+ case "model_request_start": {
59
+ // Create new assistant message placeholder
60
+ const newMsgId = nanoid();
61
+ setCurrentMessageId(newMsgId);
62
+ addMessage({
63
+ id: newMsgId,
64
+ role: "assistant",
65
+ content: "",
66
+ timestamp: new Date(),
67
+ isStreaming: true,
68
+ toolCalls: [],
69
+ });
70
+ break;
71
+ }
72
+
73
+ case "text_delta": {
74
+ // Append text delta to current message
75
+ if (currentMessageId) {
76
+ const content = (wsEvent.data as { index: number; content: string }).content;
77
+ updateMessage(currentMessageId, (msg) => ({
78
+ ...msg,
79
+ content: msg.content + content,
80
+ }));
81
+ }
82
+ break;
83
+ }
84
+
85
+ case "tool_call": {
86
+ // Add tool call to current message
87
+ if (currentMessageId) {
88
+ const { tool_name, args, tool_call_id } = wsEvent.data as {
89
+ tool_name: string;
90
+ args: Record<string, unknown>;
91
+ tool_call_id: string;
92
+ };
93
+ const toolCall: ToolCall = {
94
+ id: tool_call_id,
95
+ name: tool_name,
96
+ args,
97
+ status: "running",
98
+ };
99
+ addToolCall(currentMessageId, toolCall);
100
+ }
101
+ break;
102
+ }
103
+
104
+ case "tool_result": {
105
+ // Update tool call with result
106
+ if (currentMessageId) {
107
+ const { tool_call_id, content } = wsEvent.data as {
108
+ tool_call_id: string;
109
+ content: string;
110
+ };
111
+ updateToolCall(currentMessageId, tool_call_id, {
112
+ result: content,
113
+ status: "completed",
114
+ });
115
+ }
116
+ break;
117
+ }
118
+
119
+ case "final_result": {
120
+ // Finalize message
121
+ if (currentMessageId) {
122
+ updateMessage(currentMessageId, (msg) => ({
123
+ ...msg,
124
+ isStreaming: false,
125
+ }));
126
+ }
127
+ setIsProcessing(false);
128
+ setCurrentMessageId(null);
129
+ break;
130
+ }
131
+
132
+ case "error": {
133
+ // Handle error
134
+ if (currentMessageId) {
135
+ updateMessage(currentMessageId, (msg) => ({
136
+ ...msg,
137
+ content: msg.content + "\n\n[Error occurred]",
138
+ isStreaming: false,
139
+ }));
140
+ }
141
+ setIsProcessing(false);
142
+ break;
143
+ }
144
+
145
+ case "complete": {
146
+ setIsProcessing(false);
147
+ break;
148
+ }
149
+ }
150
+ },
151
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
152
+ [currentMessageId, addMessage, updateMessage, addToolCall, updateToolCall, setCurrentConversationId, onConversationCreated]
153
+ {%- else %}
154
+ [currentMessageId, addMessage, updateMessage, addToolCall, updateToolCall]
155
+ {%- endif %}
156
+ );
157
+
158
+ const wsUrl = `${WS_URL}/api/v1/ws/agent`;
159
+
160
+ const { isConnected, connect, disconnect, sendMessage } = useWebSocket({
161
+ url: wsUrl,
162
+ onMessage: handleWebSocketMessage,
163
+ });
164
+
165
+ const sendChatMessage = useCallback(
166
+ (content: string) => {
167
+ // Add user message
168
+ const userMessage: ChatMessage = {
169
+ id: nanoid(),
170
+ role: "user",
171
+ content,
172
+ timestamp: new Date(),
173
+ };
174
+ addMessage(userMessage);
175
+
176
+ // Send to WebSocket
177
+ setIsProcessing(true);
178
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
179
+ sendMessage({
180
+ message: content,
181
+ conversation_id: conversationId || null,
182
+ });
183
+ {%- else %}
184
+ sendMessage({ message: content });
185
+ {%- endif %}
186
+ },
187
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
188
+ [addMessage, sendMessage, conversationId]
189
+ {%- else %}
190
+ [addMessage, sendMessage]
191
+ {%- endif %}
192
+ );
193
+
194
+ return {
195
+ messages,
196
+ isConnected,
197
+ isProcessing,
198
+ connect,
199
+ disconnect,
200
+ sendMessage: sendChatMessage,
201
+ clearMessages,
202
+ };
203
+ }
@@ -0,0 +1,175 @@
1
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
2
+ "use client";
3
+
4
+ import { useCallback } from "react";
5
+ import { apiClient } from "@/lib/api-client";
6
+ import { useConversationStore } from "@/stores";
7
+ import type {
8
+ Conversation,
9
+ ConversationMessage,
10
+ ConversationListResponse,
11
+ } from "@/types";
12
+
13
+ interface CreateConversationResponse {
14
+ id: string;
15
+ title?: string;
16
+ created_at: string;
17
+ updated_at: string;
18
+ is_archived: boolean;
19
+ }
20
+
21
+ interface MessagesResponse {
22
+ items: ConversationMessage[];
23
+ total: number;
24
+ }
25
+
26
+ export function useConversations() {
27
+ const {
28
+ conversations,
29
+ currentConversationId,
30
+ currentMessages,
31
+ isLoading,
32
+ error,
33
+ setConversations,
34
+ addConversation,
35
+ updateConversation,
36
+ removeConversation,
37
+ setCurrentConversationId,
38
+ setCurrentMessages,
39
+ setLoading,
40
+ setError,
41
+ } = useConversationStore();
42
+
43
+ const fetchConversations = useCallback(async () => {
44
+ setLoading(true);
45
+ setError(null);
46
+ try {
47
+ const response = await apiClient.get<ConversationListResponse>(
48
+ "/conversations"
49
+ );
50
+ setConversations(response.items);
51
+ } catch (err) {
52
+ const message =
53
+ err instanceof Error ? err.message : "Failed to fetch conversations";
54
+ setError(message);
55
+ } finally {
56
+ setLoading(false);
57
+ }
58
+ }, [setConversations, setLoading, setError]);
59
+
60
+ const createConversation = useCallback(
61
+ async (title?: string): Promise<Conversation | null> => {
62
+ setLoading(true);
63
+ setError(null);
64
+ try {
65
+ const response = await apiClient.post<CreateConversationResponse>(
66
+ "/conversations",
67
+ { title }
68
+ );
69
+ const newConversation: Conversation = {
70
+ id: response.id,
71
+ title: response.title,
72
+ created_at: response.created_at,
73
+ updated_at: response.updated_at,
74
+ is_archived: response.is_archived,
75
+ };
76
+ addConversation(newConversation);
77
+ return newConversation;
78
+ } catch (err) {
79
+ const message =
80
+ err instanceof Error ? err.message : "Failed to create conversation";
81
+ setError(message);
82
+ return null;
83
+ } finally {
84
+ setLoading(false);
85
+ }
86
+ },
87
+ [addConversation, setLoading, setError]
88
+ );
89
+
90
+ const selectConversation = useCallback(
91
+ async (id: string) => {
92
+ setCurrentConversationId(id);
93
+ setLoading(true);
94
+ setError(null);
95
+ try {
96
+ const response = await apiClient.get<MessagesResponse>(
97
+ `/conversations/${id}/messages`
98
+ );
99
+ setCurrentMessages(response.items);
100
+ } catch (err) {
101
+ const message =
102
+ err instanceof Error ? err.message : "Failed to fetch messages";
103
+ setError(message);
104
+ } finally {
105
+ setLoading(false);
106
+ }
107
+ },
108
+ [setCurrentConversationId, setCurrentMessages, setLoading, setError]
109
+ );
110
+
111
+ const archiveConversation = useCallback(
112
+ async (id: string) => {
113
+ try {
114
+ await apiClient.patch(`/conversations/${id}`, { is_archived: true });
115
+ updateConversation(id, { is_archived: true });
116
+ } catch (err) {
117
+ const message =
118
+ err instanceof Error ? err.message : "Failed to archive conversation";
119
+ setError(message);
120
+ }
121
+ },
122
+ [updateConversation, setError]
123
+ );
124
+
125
+ const deleteConversation = useCallback(
126
+ async (id: string) => {
127
+ try {
128
+ await apiClient.delete(`/conversations/${id}`);
129
+ removeConversation(id);
130
+ } catch (err) {
131
+ const message =
132
+ err instanceof Error ? err.message : "Failed to delete conversation";
133
+ setError(message);
134
+ }
135
+ },
136
+ [removeConversation, setError]
137
+ );
138
+
139
+ const renameConversation = useCallback(
140
+ async (id: string, title: string) => {
141
+ try {
142
+ await apiClient.patch(`/conversations/${id}`, { title });
143
+ updateConversation(id, { title });
144
+ } catch (err) {
145
+ const message =
146
+ err instanceof Error ? err.message : "Failed to rename conversation";
147
+ setError(message);
148
+ }
149
+ },
150
+ [updateConversation, setError]
151
+ );
152
+
153
+ const startNewChat = useCallback(() => {
154
+ setCurrentConversationId(null);
155
+ setCurrentMessages([]);
156
+ }, [setCurrentConversationId, setCurrentMessages]);
157
+
158
+ return {
159
+ conversations,
160
+ currentConversationId,
161
+ currentMessages,
162
+ isLoading,
163
+ error,
164
+ fetchConversations,
165
+ createConversation,
166
+ selectConversation,
167
+ archiveConversation,
168
+ deleteConversation,
169
+ renameConversation,
170
+ startNewChat,
171
+ };
172
+ }
173
+ {%- else %}
174
+ // Conversations hook - not configured (enable_conversation_persistence is false)
175
+ {%- endif %}
@@ -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,32 @@
1
+ {%- if cookiecutter.enable_i18n %}
2
+ import { notFound } from 'next/navigation';
3
+ import { getRequestConfig } from 'next-intl/server';
4
+
5
+ // Supported locales
6
+ export const locales = ['en', 'pl'] as const;
7
+ export type Locale = (typeof locales)[number];
8
+
9
+ export const defaultLocale: Locale = 'en';
10
+
11
+ export default getRequestConfig(async ({ locale }) => {
12
+ // Validate that the incoming `locale` parameter is valid
13
+ if (!locales.includes(locale as Locale)) notFound();
14
+
15
+ return {
16
+ messages: (await import(`../messages/${locale}.json`)).default
17
+ };
18
+ });
19
+
20
+ export function getLocaleLabel(locale: Locale): string {
21
+ const labels: Record<Locale, string> = {
22
+ en: 'English',
23
+ pl: 'Polski',
24
+ };
25
+ return labels[locale];
26
+ }
27
+ {%- else %}
28
+ // i18n is disabled
29
+ export const locales = ['en'] as const;
30
+ export type Locale = (typeof locales)[number];
31
+ export const defaultLocale: Locale = 'en';
32
+ {%- 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";