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,143 @@
1
+ """Tests for core modules."""
2
+
3
+ from app.core.config import settings
4
+ from app.core.exceptions import (
5
+ AlreadyExistsError,
6
+ AppException,
7
+ AuthenticationError,
8
+ AuthorizationError,
9
+ NotFoundError,
10
+ ValidationError,
11
+ )
12
+
13
+
14
+ class TestSettings:
15
+ """Tests for settings configuration."""
16
+
17
+ def test_project_name_is_set(self):
18
+ """Test project name is configured."""
19
+ assert settings.PROJECT_NAME == "{{ cookiecutter.project_name }}"
20
+
21
+ def test_api_v1_str_is_set(self):
22
+ """Test API version string is set."""
23
+ assert settings.API_V1_STR == "/api/v1"
24
+
25
+ def test_debug_mode_default(self):
26
+ """Test debug mode has default value."""
27
+ assert isinstance(settings.DEBUG, bool)
28
+
29
+ {%- if cookiecutter.enable_cors %}
30
+ def test_cors_origins_is_list(self):
31
+ """Test CORS origins is a list."""
32
+ assert isinstance(settings.CORS_ORIGINS, list)
33
+ {%- endif %}
34
+
35
+
36
+ class TestExceptions:
37
+ """Tests for custom exceptions."""
38
+
39
+ def test_app_exception(self):
40
+ """Test AppException initialization."""
41
+ error = AppException(message="Test error", code="TEST_ERROR")
42
+ assert error.message == "Test error"
43
+ assert error.code == "TEST_ERROR"
44
+ assert str(error) == "Test error"
45
+
46
+ def test_not_found_error(self):
47
+ """Test NotFoundError."""
48
+ error = NotFoundError(message="Item not found")
49
+ assert error.status_code == 404
50
+ assert error.code == "NOT_FOUND"
51
+
52
+ def test_already_exists_error(self):
53
+ """Test AlreadyExistsError."""
54
+ error = AlreadyExistsError(message="Item already exists")
55
+ assert error.status_code == 409
56
+ assert error.code == "ALREADY_EXISTS"
57
+
58
+ def test_authentication_error(self):
59
+ """Test AuthenticationError."""
60
+ error = AuthenticationError(message="Invalid credentials")
61
+ assert error.status_code == 401
62
+ assert error.code == "AUTHENTICATION_ERROR"
63
+
64
+ def test_authorization_error(self):
65
+ """Test AuthorizationError."""
66
+ error = AuthorizationError(message="Not authorized")
67
+ assert error.status_code == 403
68
+ assert error.code == "AUTHORIZATION_ERROR"
69
+
70
+ def test_validation_error(self):
71
+ """Test ValidationError."""
72
+ error = ValidationError(message="Invalid input")
73
+ assert error.status_code == 422
74
+ assert error.code == "VALIDATION_ERROR"
75
+
76
+
77
+ {%- if cookiecutter.enable_redis %}
78
+
79
+
80
+ class TestCacheSetup:
81
+ """Tests for cache setup."""
82
+
83
+ def test_setup_cache_function_exists(self):
84
+ """Test setup_cache function exists."""
85
+ from app.core.cache import setup_cache
86
+
87
+ assert setup_cache is not None
88
+ assert callable(setup_cache)
89
+ {%- endif %}
90
+
91
+
92
+ class TestMiddleware:
93
+ """Tests for middleware."""
94
+
95
+ def test_request_id_middleware_exists(self):
96
+ """Test request ID middleware is configured."""
97
+ from app.core.middleware import RequestIDMiddleware
98
+
99
+ assert RequestIDMiddleware is not None
100
+
101
+
102
+ {%- if cookiecutter.enable_rate_limiting %}
103
+
104
+
105
+ class TestRateLimit:
106
+ """Tests for rate limiting."""
107
+
108
+ def test_limiter_exists(self):
109
+ """Test rate limiter is configured."""
110
+ from app.core.rate_limit import limiter
111
+
112
+ assert limiter is not None
113
+ {%- endif %}
114
+
115
+
116
+ {%- if cookiecutter.enable_logfire %}
117
+
118
+
119
+ from unittest.mock import patch # noqa: E402
120
+
121
+
122
+ class TestLogfireSetup:
123
+ """Tests for Logfire setup."""
124
+
125
+ @patch("app.core.logfire_setup.logfire")
126
+ def test_setup_logfire_configures(self, mock_logfire):
127
+ """Test setup_logfire calls configure."""
128
+ from app.core.logfire_setup import setup_logfire
129
+
130
+ setup_logfire()
131
+ mock_logfire.configure.assert_called_once()
132
+
133
+ @patch("app.core.logfire_setup.logfire")
134
+ def test_instrument_app_instruments_fastapi(self, mock_logfire):
135
+ """Test instrument_app instruments FastAPI."""
136
+ from fastapi import FastAPI
137
+
138
+ from app.core.logfire_setup import instrument_app
139
+
140
+ app = FastAPI()
141
+ instrument_app(app)
142
+ mock_logfire.instrument_fastapi.assert_called()
143
+ {%- endif %}
@@ -0,0 +1,118 @@
1
+ """Tests for pipeline infrastructure."""
2
+
3
+ import pytest
4
+
5
+ from app.pipelines.base import BasePipeline, PipelineResult
6
+
7
+
8
+ class TestPipelineResult:
9
+ """Tests for PipelineResult dataclass."""
10
+
11
+ def test_success_rate_all_processed(self):
12
+ """Test success rate when all items processed."""
13
+ result = PipelineResult(processed=10, failed=0)
14
+ assert result.success_rate == 100.0
15
+
16
+ def test_success_rate_with_failures(self):
17
+ """Test success rate with some failures."""
18
+ result = PipelineResult(processed=8, failed=2)
19
+ assert result.success_rate == 80.0
20
+
21
+ def test_success_rate_all_failed(self):
22
+ """Test success rate when all items failed."""
23
+ result = PipelineResult(processed=0, failed=10)
24
+ assert result.success_rate == 0.0
25
+
26
+ def test_success_rate_empty(self):
27
+ """Test success rate with no items."""
28
+ result = PipelineResult(processed=0, failed=0)
29
+ assert result.success_rate == 100.0
30
+
31
+ def test_has_errors_with_failures(self):
32
+ """Test has_errors returns True when failures exist."""
33
+ result = PipelineResult(processed=5, failed=1)
34
+ assert result.has_errors is True
35
+
36
+ def test_has_errors_with_error_messages(self):
37
+ """Test has_errors returns True when error messages exist."""
38
+ result = PipelineResult(processed=5, failed=0, errors=["Error 1"])
39
+ assert result.has_errors is True
40
+
41
+ def test_has_errors_no_errors(self):
42
+ """Test has_errors returns False when no errors."""
43
+ result = PipelineResult(processed=5, failed=0)
44
+ assert result.has_errors is False
45
+
46
+ def test_default_values(self):
47
+ """Test default values are set correctly."""
48
+ result = PipelineResult(processed=5)
49
+ assert result.failed == 0
50
+ assert result.errors == []
51
+ assert result.metadata == {}
52
+
53
+
54
+ class TestBasePipeline:
55
+ """Tests for BasePipeline abstract class."""
56
+
57
+ @pytest.mark.anyio
58
+ async def test_validate_returns_true_by_default(self):
59
+ """Test validate method returns True by default."""
60
+
61
+ class TestPipeline(BasePipeline):
62
+ async def run(self) -> PipelineResult:
63
+ return PipelineResult(processed=0)
64
+
65
+ pipeline = TestPipeline()
66
+ assert await pipeline.validate() is True
67
+
68
+ @pytest.mark.anyio
69
+ async def test_cleanup_does_nothing_by_default(self):
70
+ """Test cleanup method does nothing by default."""
71
+
72
+ class TestPipeline(BasePipeline):
73
+ async def run(self) -> PipelineResult:
74
+ return PipelineResult(processed=0)
75
+
76
+ pipeline = TestPipeline()
77
+ await pipeline.cleanup() # Should not raise
78
+
79
+ @pytest.mark.anyio
80
+ async def test_run_must_be_implemented(self):
81
+ """Test that run method must be implemented by subclasses."""
82
+ # This test verifies the abstract method requirement
83
+ with pytest.raises(TypeError, match="Can't instantiate abstract class"):
84
+ BasePipeline()
85
+
86
+ @pytest.mark.anyio
87
+ async def test_custom_pipeline_implementation(self):
88
+ """Test a custom pipeline implementation."""
89
+
90
+ class MyPipeline(BasePipeline):
91
+ def __init__(self, items: list):
92
+ self.items = items
93
+
94
+ async def run(self) -> PipelineResult:
95
+ processed = 0
96
+ failed = 0
97
+ errors = []
98
+
99
+ for item in self.items:
100
+ if item > 0:
101
+ processed += 1
102
+ else:
103
+ failed += 1
104
+ errors.append(f"Invalid item: {item}")
105
+
106
+ return PipelineResult(
107
+ processed=processed,
108
+ failed=failed,
109
+ errors=errors,
110
+ )
111
+
112
+ pipeline = MyPipeline([1, 2, 3, -1, 5])
113
+ result = await pipeline.run()
114
+
115
+ assert result.processed == 4
116
+ assert result.failed == 1
117
+ assert len(result.errors) == 1
118
+ assert result.success_rate == 80.0
@@ -0,0 +1,181 @@
1
+ {%- if cookiecutter.use_database %}
2
+ """Tests for repository layer."""
3
+
4
+ from unittest.mock import AsyncMock, MagicMock
5
+ from uuid import uuid4
6
+
7
+ import pytest
8
+ from pydantic import BaseModel
9
+
10
+ from app.repositories.base import BaseRepository
11
+
12
+
13
+ class MockModel:
14
+ """Mock SQLAlchemy model for testing."""
15
+
16
+ def __init__(self, **kwargs):
17
+ self.id = kwargs.get("id", uuid4())
18
+ for key, value in kwargs.items():
19
+ setattr(self, key, value)
20
+
21
+
22
+ class MockCreateSchema(BaseModel):
23
+ """Mock create schema."""
24
+
25
+ name: str
26
+
27
+
28
+ class MockUpdateSchema(BaseModel):
29
+ """Mock update schema."""
30
+
31
+ name: str | None = None
32
+
33
+
34
+ class TestBaseRepository:
35
+ """Tests for BaseRepository."""
36
+
37
+ @pytest.fixture
38
+ def repository(self):
39
+ """Create a test repository."""
40
+ return BaseRepository[MockModel, MockCreateSchema, MockUpdateSchema](MockModel)
41
+
42
+ @pytest.fixture
43
+ def mock_session(self):
44
+ """Create a mock async session."""
45
+ session = MagicMock()
46
+ session.get = AsyncMock()
47
+ session.execute = AsyncMock()
48
+ session.add = MagicMock()
49
+ session.flush = AsyncMock()
50
+ session.refresh = AsyncMock()
51
+ session.delete = AsyncMock()
52
+ return session
53
+
54
+ @pytest.mark.anyio
55
+ async def test_get_returns_model(self, repository, mock_session):
56
+ """Test get returns a model by ID."""
57
+ mock_obj = MockModel(name="test")
58
+ mock_session.get.return_value = mock_obj
59
+
60
+ result = await repository.get(mock_session, mock_obj.id)
61
+
62
+ assert result == mock_obj
63
+ mock_session.get.assert_called_once_with(MockModel, mock_obj.id)
64
+
65
+ @pytest.mark.anyio
66
+ async def test_get_returns_none_when_not_found(self, repository, mock_session):
67
+ """Test get returns None when not found."""
68
+ mock_session.get.return_value = None
69
+
70
+ result = await repository.get(mock_session, uuid4())
71
+
72
+ assert result is None
73
+
74
+ # Note: test_get_multi_returns_list is skipped because it requires a real
75
+ # SQLAlchemy model. The select() function cannot work with a mock class.
76
+ # For proper integration testing, use actual SQLAlchemy models with a test DB.
77
+
78
+ @pytest.mark.anyio
79
+ async def test_create_adds_and_returns_model(self, repository, mock_session):
80
+ """Test create adds a new model."""
81
+ create_data = MockCreateSchema(name="new item")
82
+
83
+ # Mock the model creation
84
+ async def refresh_side_effect(obj):
85
+ obj.id = uuid4()
86
+
87
+ mock_session.refresh.side_effect = refresh_side_effect
88
+
89
+ result = await repository.create(mock_session, obj_in=create_data)
90
+
91
+ assert result.name == "new item"
92
+ mock_session.add.assert_called_once()
93
+ mock_session.flush.assert_called_once()
94
+ mock_session.refresh.assert_called_once()
95
+
96
+ @pytest.mark.anyio
97
+ async def test_update_with_schema(self, repository, mock_session):
98
+ """Test update with Pydantic schema."""
99
+ db_obj = MockModel(name="old name")
100
+ update_data = MockUpdateSchema(name="new name")
101
+
102
+ result = await repository.update(mock_session, db_obj=db_obj, obj_in=update_data)
103
+
104
+ assert result.name == "new name"
105
+ mock_session.add.assert_called_once()
106
+ mock_session.flush.assert_called_once()
107
+
108
+ @pytest.mark.anyio
109
+ async def test_update_with_dict(self, repository, mock_session):
110
+ """Test update with dictionary."""
111
+ db_obj = MockModel(name="old name")
112
+ update_data = {"name": "new name"}
113
+
114
+ result = await repository.update(mock_session, db_obj=db_obj, obj_in=update_data)
115
+
116
+ assert result.name == "new name"
117
+
118
+ @pytest.mark.anyio
119
+ async def test_delete_removes_and_returns_model(self, repository, mock_session):
120
+ """Test delete removes and returns model."""
121
+ mock_obj = MockModel(name="to delete")
122
+ mock_session.get.return_value = mock_obj
123
+
124
+ result = await repository.delete(mock_session, id=mock_obj.id)
125
+
126
+ assert result == mock_obj
127
+ mock_session.delete.assert_called_once_with(mock_obj)
128
+ mock_session.flush.assert_called_once()
129
+
130
+ @pytest.mark.anyio
131
+ async def test_delete_returns_none_when_not_found(self, repository, mock_session):
132
+ """Test delete returns None when not found."""
133
+ mock_session.get.return_value = None
134
+
135
+ result = await repository.delete(mock_session, id=uuid4())
136
+
137
+ assert result is None
138
+ mock_session.delete.assert_not_called()
139
+
140
+
141
+ {%- if cookiecutter.use_jwt %}
142
+
143
+
144
+ class TestUserRepository:
145
+ """Tests for user repository functions."""
146
+
147
+ @pytest.fixture
148
+ def mock_session(self):
149
+ """Create a mock async session."""
150
+ session = MagicMock()
151
+ session.execute = AsyncMock()
152
+ return session
153
+
154
+ @pytest.mark.anyio
155
+ async def test_get_by_email(self, mock_session):
156
+ """Test get_by_email returns user."""
157
+ from app.repositories import user as user_repo
158
+
159
+ mock_user = MagicMock()
160
+ mock_result = MagicMock()
161
+ mock_result.scalar_one_or_none.return_value = mock_user
162
+ mock_session.execute.return_value = mock_result
163
+
164
+ result = await user_repo.get_by_email(mock_session, "test@example.com")
165
+
166
+ assert result == mock_user
167
+
168
+ @pytest.mark.anyio
169
+ async def test_get_by_email_not_found(self, mock_session):
170
+ """Test get_by_email returns None when not found."""
171
+ from app.repositories import user as user_repo
172
+
173
+ mock_result = MagicMock()
174
+ mock_result.scalar_one_or_none.return_value = None
175
+ mock_session.execute.return_value = mock_result
176
+
177
+ result = await user_repo.get_by_email(mock_session, "notfound@example.com")
178
+
179
+ assert result is None
180
+ {%- endif %}
181
+ {%- endif %}
@@ -0,0 +1,124 @@
1
+ {%- if cookiecutter.use_jwt %}
2
+ """Tests for security module."""
3
+
4
+ from datetime import timedelta
5
+
6
+ from app.core.security import (
7
+ create_access_token,
8
+ create_refresh_token,
9
+ get_password_hash,
10
+ verify_password,
11
+ verify_token,
12
+ )
13
+
14
+
15
+ class TestPasswordHashing:
16
+ """Tests for password hashing functions."""
17
+
18
+ def test_hash_password(self):
19
+ """Test password hashing."""
20
+ password = "mysecretpassword"
21
+ hashed = get_password_hash(password)
22
+
23
+ assert hashed != password
24
+ assert len(hashed) > 0
25
+ assert hashed.startswith("$2") # bcrypt prefix
26
+
27
+ def test_verify_password_correct(self):
28
+ """Test verifying correct password."""
29
+ password = "mysecretpassword"
30
+ hashed = get_password_hash(password)
31
+
32
+ assert verify_password(password, hashed) is True
33
+
34
+ def test_verify_password_incorrect(self):
35
+ """Test verifying incorrect password."""
36
+ password = "mysecretpassword"
37
+ wrong_password = "wrongpassword"
38
+ hashed = get_password_hash(password)
39
+
40
+ assert verify_password(wrong_password, hashed) is False
41
+
42
+
43
+ class TestAccessToken:
44
+ """Tests for access token functions."""
45
+
46
+ def test_create_access_token(self):
47
+ """Test creating access token."""
48
+ subject = "user123"
49
+ token = create_access_token(subject)
50
+
51
+ assert isinstance(token, str)
52
+ assert len(token) > 0
53
+
54
+ def test_create_access_token_with_expires_delta(self):
55
+ """Test creating access token with custom expiration."""
56
+ subject = "user123"
57
+ expires = timedelta(hours=2)
58
+ token = create_access_token(subject, expires_delta=expires)
59
+
60
+ assert isinstance(token, str)
61
+ payload = verify_token(token)
62
+ assert payload is not None
63
+ assert payload["sub"] == subject
64
+ assert payload["type"] == "access"
65
+
66
+ def test_verify_access_token(self):
67
+ """Test verifying access token."""
68
+ subject = "user123"
69
+ token = create_access_token(subject)
70
+ payload = verify_token(token)
71
+
72
+ assert payload is not None
73
+ assert payload["sub"] == subject
74
+ assert payload["type"] == "access"
75
+
76
+ def test_verify_invalid_token(self):
77
+ """Test verifying invalid token."""
78
+ payload = verify_token("invalid.token.here")
79
+
80
+ assert payload is None
81
+
82
+ def test_verify_expired_token(self):
83
+ """Test verifying expired token."""
84
+ subject = "user123"
85
+ # Create token that expires immediately
86
+ token = create_access_token(subject, expires_delta=timedelta(seconds=-1))
87
+ payload = verify_token(token)
88
+
89
+ assert payload is None
90
+
91
+
92
+ class TestRefreshToken:
93
+ """Tests for refresh token functions."""
94
+
95
+ def test_create_refresh_token(self):
96
+ """Test creating refresh token."""
97
+ subject = "user123"
98
+ token = create_refresh_token(subject)
99
+
100
+ assert isinstance(token, str)
101
+ assert len(token) > 0
102
+
103
+ def test_create_refresh_token_with_expires_delta(self):
104
+ """Test creating refresh token with custom expiration."""
105
+ subject = "user123"
106
+ expires = timedelta(days=7)
107
+ token = create_refresh_token(subject, expires_delta=expires)
108
+
109
+ assert isinstance(token, str)
110
+ payload = verify_token(token)
111
+ assert payload is not None
112
+ assert payload["sub"] == subject
113
+ assert payload["type"] == "refresh"
114
+
115
+ def test_verify_refresh_token(self):
116
+ """Test verifying refresh token."""
117
+ subject = "user123"
118
+ token = create_refresh_token(subject)
119
+ payload = verify_token(token)
120
+
121
+ assert payload is not None
122
+ assert payload["sub"] == subject
123
+ assert payload["type"] == "refresh"
124
+ {%- endif %}