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,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,33 @@
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
+ // Prefix the default locale (e.g., /en/about instead of /about)
13
+ localePrefix: 'always',
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
+ // However, match all pathnames within `/api`, except for webhooks
27
+ // '/api/((?!webhooks).*)',
28
+ ],
29
+ };
30
+ {%- else %}
31
+ // Middleware is disabled when i18n is not enabled
32
+ export { };
33
+ {%- endif %}
@@ -0,0 +1,72 @@
1
+ {%- if cookiecutter.use_frontend and cookiecutter.use_jwt %}
2
+ import { describe, it, expect, beforeEach } from "vitest";
3
+ import { useAuthStore } from "./auth-store";
4
+ import type { User } from "@/types";
5
+
6
+ const createMockUser = (overrides?: Partial<User>): User => ({
7
+ id: "test-id",
8
+ email: "test@example.com",
9
+ is_active: true,
10
+ created_at: new Date().toISOString(),
11
+ ...overrides,
12
+ });
13
+
14
+ describe("Auth Store", () => {
15
+ beforeEach(() => {
16
+ // Reset store before each test
17
+ useAuthStore.setState({
18
+ user: null,
19
+ isAuthenticated: false,
20
+ isLoading: false,
21
+ });
22
+ });
23
+
24
+ it("should have initial state", () => {
25
+ const state = useAuthStore.getState();
26
+ expect(state.user).toBeNull();
27
+ expect(state.isAuthenticated).toBe(false);
28
+ expect(state.isLoading).toBe(false);
29
+ });
30
+
31
+ it("should set user on setUser", () => {
32
+ const testUser = createMockUser();
33
+
34
+ useAuthStore.getState().setUser(testUser);
35
+
36
+ const state = useAuthStore.getState();
37
+ expect(state.user).toEqual(testUser);
38
+ expect(state.isAuthenticated).toBe(true);
39
+ });
40
+
41
+ it("should clear user on logout", () => {
42
+ // First set a user
43
+ useAuthStore.getState().setUser(createMockUser());
44
+
45
+ // Then logout
46
+ useAuthStore.getState().logout();
47
+
48
+ const state = useAuthStore.getState();
49
+ expect(state.user).toBeNull();
50
+ expect(state.isAuthenticated).toBe(false);
51
+ });
52
+
53
+ it("should set loading state", () => {
54
+ useAuthStore.getState().setLoading(true);
55
+ expect(useAuthStore.getState().isLoading).toBe(true);
56
+
57
+ useAuthStore.getState().setLoading(false);
58
+ expect(useAuthStore.getState().isLoading).toBe(false);
59
+ });
60
+ });
61
+ {%- elif cookiecutter.use_frontend %}
62
+ import { describe, it, expect } from "vitest";
63
+
64
+ describe("Auth Store", () => {
65
+ it.skip("JWT authentication not enabled", () => {
66
+ // Skip auth store tests when JWT is not configured
67
+ });
68
+ });
69
+ {%- else %}
70
+ /* Auth store tests - frontend not configured */
71
+ export {};
72
+ {%- endif %}
@@ -0,0 +1,48 @@
1
+ "use client";
2
+
3
+ import { create } from "zustand";
4
+ import { persist } from "zustand/middleware";
5
+ import type { User } from "@/types";
6
+
7
+ interface AuthState {
8
+ user: User | null;
9
+ isAuthenticated: boolean;
10
+ isLoading: boolean;
11
+
12
+ setUser: (user: User | null) => void;
13
+ setLoading: (loading: boolean) => void;
14
+ logout: () => void;
15
+ }
16
+
17
+ export const useAuthStore = create<AuthState>()(
18
+ persist(
19
+ (set) => ({
20
+ user: null,
21
+ isAuthenticated: false,
22
+ isLoading: true,
23
+
24
+ setUser: (user) =>
25
+ set({
26
+ user,
27
+ isAuthenticated: user !== null,
28
+ isLoading: false,
29
+ }),
30
+
31
+ setLoading: (loading) => set({ isLoading: loading }),
32
+
33
+ logout: () =>
34
+ set({
35
+ user: null,
36
+ isAuthenticated: false,
37
+ isLoading: false,
38
+ }),
39
+ }),
40
+ {
41
+ name: "auth-storage",
42
+ partialize: (state) => ({
43
+ user: state.user,
44
+ isAuthenticated: state.isAuthenticated,
45
+ }),
46
+ }
47
+ )
48
+ );
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import { create } from "zustand";
4
+ import type { ChatMessage, ToolCall } from "@/types";
5
+
6
+ interface ChatState {
7
+ messages: ChatMessage[];
8
+ isStreaming: boolean;
9
+
10
+ addMessage: (message: ChatMessage) => void;
11
+ updateMessage: (
12
+ id: string,
13
+ updater: (msg: ChatMessage) => ChatMessage
14
+ ) => void;
15
+ addToolCall: (messageId: string, toolCall: ToolCall) => void;
16
+ updateToolCall: (
17
+ messageId: string,
18
+ toolCallId: string,
19
+ update: Partial<ToolCall>
20
+ ) => void;
21
+ setStreaming: (streaming: boolean) => void;
22
+ clearMessages: () => void;
23
+ }
24
+
25
+ export const useChatStore = create<ChatState>((set) => ({
26
+ messages: [],
27
+ isStreaming: false,
28
+
29
+ addMessage: (message) =>
30
+ set((state) => ({
31
+ messages: [...state.messages, message],
32
+ })),
33
+
34
+ updateMessage: (id, updater) =>
35
+ set((state) => ({
36
+ messages: state.messages.map((msg) => (msg.id === id ? updater(msg) : msg)),
37
+ })),
38
+
39
+ addToolCall: (messageId, toolCall) =>
40
+ set((state) => ({
41
+ messages: state.messages.map((msg) =>
42
+ msg.id === messageId
43
+ ? { ...msg, toolCalls: [...(msg.toolCalls || []), toolCall] }
44
+ : msg
45
+ ),
46
+ })),
47
+
48
+ updateToolCall: (messageId, toolCallId, update) =>
49
+ set((state) => ({
50
+ messages: state.messages.map((msg) =>
51
+ msg.id === messageId
52
+ ? {
53
+ ...msg,
54
+ toolCalls: msg.toolCalls?.map((tc) =>
55
+ tc.id === toolCallId ? { ...tc, ...update } : tc
56
+ ),
57
+ }
58
+ : msg
59
+ ),
60
+ })),
61
+
62
+ setStreaming: (streaming) => set({ isStreaming: streaming }),
63
+
64
+ clearMessages: () => set({ messages: [] }),
65
+ }));
@@ -0,0 +1,76 @@
1
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
2
+ "use client";
3
+
4
+ import { create } from "zustand";
5
+ import type { Conversation, ConversationMessage } from "@/types";
6
+
7
+ interface ConversationState {
8
+ conversations: Conversation[];
9
+ currentConversationId: string | null;
10
+ currentMessages: ConversationMessage[];
11
+ isLoading: boolean;
12
+ error: string | null;
13
+
14
+ // Actions
15
+ setConversations: (conversations: Conversation[]) => void;
16
+ addConversation: (conversation: Conversation) => void;
17
+ updateConversation: (id: string, updates: Partial<Conversation>) => void;
18
+ removeConversation: (id: string) => void;
19
+ setCurrentConversationId: (id: string | null) => void;
20
+ setCurrentMessages: (messages: ConversationMessage[]) => void;
21
+ addMessage: (message: ConversationMessage) => void;
22
+ setLoading: (loading: boolean) => void;
23
+ setError: (error: string | null) => void;
24
+ reset: () => void;
25
+ }
26
+
27
+ const initialState = {
28
+ conversations: [],
29
+ currentConversationId: null,
30
+ currentMessages: [],
31
+ isLoading: false,
32
+ error: null,
33
+ };
34
+
35
+ export const useConversationStore = create<ConversationState>((set) => ({
36
+ ...initialState,
37
+
38
+ setConversations: (conversations) => set({ conversations }),
39
+
40
+ addConversation: (conversation) =>
41
+ set((state) => ({
42
+ conversations: [conversation, ...state.conversations],
43
+ })),
44
+
45
+ updateConversation: (id, updates) =>
46
+ set((state) => ({
47
+ conversations: state.conversations.map((conv) =>
48
+ conv.id === id ? { ...conv, ...updates } : conv
49
+ ),
50
+ })),
51
+
52
+ removeConversation: (id) =>
53
+ set((state) => ({
54
+ conversations: state.conversations.filter((conv) => conv.id !== id),
55
+ currentConversationId:
56
+ state.currentConversationId === id ? null : state.currentConversationId,
57
+ })),
58
+
59
+ setCurrentConversationId: (id) => set({ currentConversationId: id }),
60
+
61
+ setCurrentMessages: (messages) => set({ currentMessages: messages }),
62
+
63
+ addMessage: (message) =>
64
+ set((state) => ({
65
+ currentMessages: [...state.currentMessages, message],
66
+ })),
67
+
68
+ setLoading: (loading) => set({ isLoading: loading }),
69
+
70
+ setError: (error) => set({ error }),
71
+
72
+ reset: () => set(initialState),
73
+ }));
74
+ {%- else %}
75
+ // Conversation store - not configured (enable_conversation_persistence is false)
76
+ {%- endif %}
@@ -0,0 +1,6 @@
1
+ export { useAuthStore } from "./auth-store";
2
+ export { useChatStore } from "./chat-store";
3
+ export { useThemeStore } from "./theme-store";
4
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
5
+ export { useConversationStore } from "./conversation-store";
6
+ {%- endif %}
@@ -0,0 +1,44 @@
1
+ {%- if cookiecutter.use_frontend %}
2
+ "use client";
3
+
4
+ import { create } from "zustand";
5
+ import { persist } from "zustand/middleware";
6
+
7
+ export type Theme = "light" | "dark" | "system";
8
+
9
+ interface ThemeState {
10
+ theme: Theme;
11
+ setTheme: (theme: Theme) => void;
12
+ }
13
+
14
+ export const useThemeStore = create<ThemeState>()(
15
+ persist(
16
+ (set) => ({
17
+ theme: "system",
18
+ setTheme: (theme) => set({ theme }),
19
+ }),
20
+ {
21
+ name: "theme-storage",
22
+ }
23
+ )
24
+ );
25
+
26
+ /**
27
+ * Get the resolved theme (light or dark) based on the current theme setting.
28
+ * When theme is "system", it checks the user's system preference.
29
+ */
30
+ export function getResolvedTheme(theme: Theme): "light" | "dark" {
31
+ if (theme === "system") {
32
+ if (typeof window !== "undefined") {
33
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
34
+ ? "dark"
35
+ : "light";
36
+ }
37
+ return "light";
38
+ }
39
+ return theme;
40
+ }
41
+ {%- else %}
42
+ /* Theme store - frontend not configured */
43
+ export {};
44
+ {%- endif %}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * API response types.
3
+ */
4
+
5
+ export interface ApiResponse<T> {
6
+ data: T;
7
+ message?: string;
8
+ }
9
+
10
+ export interface PaginatedResponse<T> {
11
+ items: T[];
12
+ total: number;
13
+ page: number;
14
+ size: number;
15
+ pages: number;
16
+ }
17
+
18
+ export interface HealthResponse {
19
+ status: string;
20
+ version?: string;
21
+ database?: string;
22
+ }
23
+
24
+ export interface ErrorResponse {
25
+ detail: string;
26
+ status_code?: number;
27
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Authentication types.
3
+ */
4
+
5
+ export interface User {
6
+ id: string;
7
+ email: string;
8
+ name?: string;
9
+ is_active: boolean;
10
+ is_superuser?: boolean;
11
+ created_at: string;
12
+ }
13
+
14
+ export interface LoginRequest {
15
+ email: string;
16
+ password: string;
17
+ }
18
+
19
+ export interface LoginResponse {
20
+ access_token: string;
21
+ refresh_token: string;
22
+ token_type: string;
23
+ user: User;
24
+ }
25
+
26
+ export interface RegisterRequest {
27
+ email: string;
28
+ password: string;
29
+ name?: string;
30
+ }
31
+
32
+ export interface RegisterResponse {
33
+ id: string;
34
+ email: string;
35
+ name?: string;
36
+ }
37
+
38
+ export interface RefreshTokenRequest {
39
+ refresh_token: string;
40
+ }
41
+
42
+ export interface RefreshTokenResponse {
43
+ access_token: string;
44
+ token_type: string;
45
+ }
46
+
47
+ export interface AuthState {
48
+ user: User | null;
49
+ accessToken: string | null;
50
+ isAuthenticated: boolean;
51
+ isLoading: boolean;
52
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Chat and AI Agent types.
3
+ */
4
+
5
+ export type MessageRole = "user" | "assistant" | "system";
6
+
7
+ export interface ChatMessage {
8
+ id: string;
9
+ role: MessageRole;
10
+ content: string;
11
+ timestamp: Date;
12
+ toolCalls?: ToolCall[];
13
+ isStreaming?: boolean;
14
+ }
15
+
16
+ export interface ToolCall {
17
+ id: string;
18
+ name: string;
19
+ args: Record<string, unknown>;
20
+ result?: unknown;
21
+ status: "pending" | "running" | "completed" | "error";
22
+ }
23
+
24
+ // WebSocket event types from backend
25
+ export type WSEventType =
26
+ | "user_prompt"
27
+ | "user_prompt_processed"
28
+ | "model_request_start"
29
+ | "part_start"
30
+ | "text_delta"
31
+ | "tool_call_delta"
32
+ | "call_tools_start"
33
+ | "tool_call"
34
+ | "tool_result"
35
+ | "final_result_start"
36
+ | "final_result"
37
+ | "complete"
38
+ | "error";
39
+
40
+ export interface WSEvent {
41
+ type: WSEventType;
42
+ data?: unknown;
43
+ timestamp?: string;
44
+ }
45
+
46
+ export interface TextDeltaEvent {
47
+ type: "text_delta";
48
+ data: {
49
+ delta: string;
50
+ };
51
+ }
52
+
53
+ export interface ToolCallEvent {
54
+ type: "tool_call";
55
+ data: {
56
+ tool_name: string;
57
+ args: Record<string, unknown>;
58
+ };
59
+ }
60
+
61
+ export interface ToolResultEvent {
62
+ type: "tool_result";
63
+ data: {
64
+ tool_name: string;
65
+ result: unknown;
66
+ };
67
+ }
68
+
69
+ export interface FinalResultEvent {
70
+ type: "final_result";
71
+ data: {
72
+ output: string;
73
+ tool_events: ToolCall[];
74
+ };
75
+ }
76
+
77
+ export interface ChatState {
78
+ messages: ChatMessage[];
79
+ isConnected: boolean;
80
+ isProcessing: boolean;
81
+ }