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,221 @@
1
+ """API versioning utilities and deprecation handling.
2
+
3
+ This module provides tools for managing API version deprecation:
4
+ - Deprecation middleware for entire API versions
5
+ - Deprecation decorator for individual endpoints
6
+ - RFC 8594 compliant deprecation headers
7
+ """
8
+
9
+ from collections.abc import Callable
10
+ from datetime import datetime
11
+ from functools import wraps
12
+
13
+ import logfire
14
+ from fastapi import Request, Response
15
+ from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
16
+
17
+
18
+ class VersionDeprecationMiddleware(BaseHTTPMiddleware):
19
+ """Middleware to add deprecation headers for deprecated API versions.
20
+
21
+ Adds RFC 8594 compliant headers:
22
+ - Deprecation: Indicates the version is deprecated
23
+ - Sunset: Indicates when the version will be removed
24
+ - Link: Points to migration documentation
25
+
26
+ Usage in main.py:
27
+ app.add_middleware(
28
+ VersionDeprecationMiddleware,
29
+ deprecated_versions={"v1": {"sunset": "2025-06-01", "link": "/docs/migration/v2"}},
30
+ )
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ app,
36
+ deprecated_versions: dict[str, dict] | None = None,
37
+ ):
38
+ """Initialize the middleware.
39
+
40
+ Args:
41
+ app: The ASGI application.
42
+ deprecated_versions: Dict mapping version prefixes to deprecation info.
43
+ Each entry should have:
44
+ - sunset: ISO date string when version will be removed (optional)
45
+ - link: URL to migration documentation (optional)
46
+ - message: Custom deprecation message (optional)
47
+
48
+ Example:
49
+ {
50
+ "v1": {
51
+ "sunset": "2025-06-01",
52
+ "link": "https://api.example.com/docs/migration/v2",
53
+ "message": "Please migrate to API v2",
54
+ }
55
+ }
56
+ """
57
+ super().__init__(app)
58
+ self.deprecated_versions = deprecated_versions or {}
59
+
60
+ async def dispatch(
61
+ self, request: Request, call_next: RequestResponseEndpoint
62
+ ) -> Response:
63
+ """Process the request and add deprecation headers if needed."""
64
+ response = await call_next(request)
65
+
66
+ # Check if request path matches a deprecated version
67
+ path = request.url.path
68
+ for version, info in self.deprecated_versions.items():
69
+ if f"/api/{version}/" in path or path.endswith(f"/api/{version}"):
70
+ self._add_deprecation_headers(response, version, info)
71
+ self._log_deprecated_usage(request, version)
72
+ break
73
+
74
+ return response
75
+
76
+ def _add_deprecation_headers(
77
+ self, response: Response, version: str, info: dict
78
+ ) -> None:
79
+ """Add RFC 8594 deprecation headers to the response."""
80
+ # Deprecation header - indicates the API is deprecated
81
+ response.headers["Deprecation"] = "true"
82
+
83
+ # Sunset header - when the API will be removed
84
+ if sunset := info.get("sunset"):
85
+ # Convert to HTTP date format
86
+ sunset_date = datetime.fromisoformat(sunset)
87
+ response.headers["Sunset"] = sunset_date.strftime("%a, %d %b %Y %H:%M:%S GMT")
88
+
89
+ # Link header - documentation for migration
90
+ if link := info.get("link"):
91
+ response.headers["Link"] = f'<{link}>; rel="deprecation"'
92
+
93
+ # Custom warning header
94
+ message = info.get("message", f"API {version} is deprecated")
95
+ response.headers["X-API-Deprecation-Warning"] = message
96
+
97
+ def _log_deprecated_usage(self, request: Request, version: str) -> None:
98
+ """Log usage of deprecated API version for monitoring."""
99
+ logfire.warn(
100
+ "Deprecated API version accessed",
101
+ version=version,
102
+ path=request.url.path,
103
+ method=request.method,
104
+ client_ip=request.client.host if request.client else None,
105
+ user_agent=request.headers.get("User-Agent"),
106
+ )
107
+
108
+
109
+ def deprecated(
110
+ sunset: str | None = None,
111
+ message: str | None = None,
112
+ link: str | None = None,
113
+ ):
114
+ """Decorator to mark an endpoint as deprecated.
115
+
116
+ Adds deprecation headers to responses from the decorated endpoint.
117
+ Use this for deprecating individual endpoints within an active API version.
118
+
119
+ Args:
120
+ sunset: ISO date string when endpoint will be removed.
121
+ message: Custom deprecation message.
122
+ link: URL to migration documentation.
123
+
124
+ Usage:
125
+ @router.get("/old-endpoint")
126
+ @deprecated(
127
+ sunset="2025-06-01",
128
+ message="Use /new-endpoint instead",
129
+ link="/docs/migration",
130
+ )
131
+ async def old_endpoint():
132
+ ...
133
+ """
134
+
135
+ def decorator(func: Callable) -> Callable:
136
+ @wraps(func)
137
+ async def wrapper(*args, **kwargs):
138
+ # Get the response from the endpoint
139
+ result = await func(*args, **kwargs)
140
+
141
+ # Find Response object in args (FastAPI injects it)
142
+ response = None
143
+ for arg in args:
144
+ if isinstance(arg, Response):
145
+ response = arg
146
+ break
147
+ for value in kwargs.values():
148
+ if isinstance(value, Response):
149
+ response = value
150
+ break
151
+
152
+ # If we have a Response object, add headers
153
+ if response:
154
+ response.headers["Deprecation"] = "true"
155
+ if sunset:
156
+ sunset_date = datetime.fromisoformat(sunset)
157
+ response.headers["Sunset"] = sunset_date.strftime(
158
+ "%a, %d %b %Y %H:%M:%S GMT"
159
+ )
160
+ if link:
161
+ response.headers["Link"] = f'<{link}>; rel="deprecation"'
162
+ if message:
163
+ response.headers["X-API-Deprecation-Warning"] = message
164
+
165
+ return result
166
+
167
+ # Add deprecation info to OpenAPI schema
168
+ wrapper.__doc__ = (
169
+ f"{func.__doc__ or ''}\n\n"
170
+ f"**DEPRECATED**"
171
+ f"{f': {message}' if message else ''}"
172
+ f"{f' (Sunset: {sunset})' if sunset else ''}"
173
+ )
174
+
175
+ return wrapper
176
+
177
+ return decorator
178
+
179
+
180
+ # Example usage documentation
181
+ """
182
+ ## Adding a New API Version
183
+
184
+ 1. Create a new version folder:
185
+ ```
186
+ app/api/routes/v2/
187
+ ├── __init__.py
188
+ ├── health.py
189
+ ├── auth.py
190
+ └── ...
191
+ ```
192
+
193
+ 2. Create the v2 router in `v2/__init__.py`:
194
+ ```python
195
+ from fastapi import APIRouter
196
+ v2_router = APIRouter()
197
+ # Include routes...
198
+ ```
199
+
200
+ 3. Add the v2 router in `app/api/router.py`:
201
+ ```python
202
+ from app.api.routes.v2 import v2_router
203
+
204
+ api_router.include_router(v1_router, prefix="/v1")
205
+ api_router.include_router(v2_router, prefix="/v2")
206
+ ```
207
+
208
+ 4. Mark v1 as deprecated in `main.py`:
209
+ ```python
210
+ app.add_middleware(
211
+ VersionDeprecationMiddleware,
212
+ deprecated_versions={
213
+ "v1": {
214
+ "sunset": "2025-12-31",
215
+ "link": "/docs/migration/v2",
216
+ "message": "Please migrate to API v2",
217
+ }
218
+ },
219
+ )
220
+ ```
221
+ """
@@ -0,0 +1,14 @@
1
+ """External service clients.
2
+
3
+ This module contains thin wrappers around external services like Redis.
4
+ """
5
+ {%- if cookiecutter.enable_redis %}
6
+
7
+ from app.clients.redis import RedisClient
8
+ {%- endif %}
9
+
10
+ __all__ = [
11
+ {%- if cookiecutter.enable_redis %}
12
+ "RedisClient",
13
+ {%- endif %}
14
+ ]
@@ -0,0 +1,88 @@
1
+ {%- if cookiecutter.enable_redis %}
2
+ """Redis client wrapper.
3
+
4
+ Provides a class-based Redis client for connection management and operations.
5
+ """
6
+
7
+ from redis import asyncio as aioredis
8
+
9
+ from app.core.config import settings
10
+
11
+
12
+ class RedisClient:
13
+ """Redis client wrapper for connection lifecycle management.
14
+
15
+ Usage in FastAPI lifespan:
16
+ async with contextmanager():
17
+ redis = RedisClient(settings.REDIS_URL)
18
+ await redis.connect()
19
+ yield {"redis": redis}
20
+ await redis.close()
21
+ """
22
+
23
+ def __init__(self, url: str | None = None):
24
+ self.url = url or settings.REDIS_URL
25
+ self.client: aioredis.Redis | None = None
26
+
27
+ async def connect(self) -> None:
28
+ """Connect to Redis server."""
29
+ self.client = aioredis.from_url(
30
+ self.url,
31
+ encoding="utf-8",
32
+ decode_responses=True,
33
+ )
34
+
35
+ async def close(self) -> None:
36
+ """Close Redis connection."""
37
+ if self.client:
38
+ await self.client.close()
39
+ self.client = None
40
+
41
+ async def get(self, key: str) -> str | None:
42
+ """Get a value by key."""
43
+ if not self.client:
44
+ raise RuntimeError("Redis client not connected")
45
+ return await self.client.get(key)
46
+
47
+ async def set(
48
+ self,
49
+ key: str,
50
+ value: str,
51
+ ttl: int | None = None,
52
+ ) -> None:
53
+ """Set a value with optional TTL (in seconds)."""
54
+ if not self.client:
55
+ raise RuntimeError("Redis client not connected")
56
+ await self.client.set(key, value, ex=ttl)
57
+
58
+ async def delete(self, key: str) -> int:
59
+ """Delete a key. Returns number of keys deleted."""
60
+ if not self.client:
61
+ raise RuntimeError("Redis client not connected")
62
+ return await self.client.delete(key)
63
+
64
+ async def exists(self, key: str) -> bool:
65
+ """Check if key exists."""
66
+ if not self.client:
67
+ raise RuntimeError("Redis client not connected")
68
+ return bool(await self.client.exists(key))
69
+
70
+ async def ping(self) -> bool:
71
+ """Ping Redis server. Returns True if connected."""
72
+ if not self.client:
73
+ return False
74
+ try:
75
+ await self.client.ping()
76
+ return True
77
+ except Exception:
78
+ return False
79
+
80
+ @property
81
+ def raw(self) -> aioredis.Redis:
82
+ """Access the underlying aioredis client for advanced operations."""
83
+ if not self.client:
84
+ raise RuntimeError("Redis client not connected")
85
+ return self.client
86
+ {%- else %}
87
+ """Redis client - not configured."""
88
+ {%- endif %}
@@ -0,0 +1,117 @@
1
+ """
2
+ Custom commands system with auto-discovery.
3
+
4
+ This module provides a Django-like custom commands system for FastAPI + Click.
5
+ Commands are auto-discovered from this package and registered to the CLI.
6
+
7
+ Usage:
8
+ # In app/commands/my_command.py
9
+ from app.commands import command
10
+ import click
11
+
12
+ @command("my-command", help="Description of my command")
13
+ @click.option("--option", "-o", help="Some option")
14
+ def my_command(option: str):
15
+ click.echo(f"Running with {option}")
16
+
17
+ # Then use it:
18
+ # project cmd my-command --option value
19
+ """
20
+
21
+ import importlib
22
+ import pkgutil
23
+ from collections.abc import Callable
24
+ from pathlib import Path
25
+
26
+ import click
27
+
28
+ # Registry for custom commands
29
+ _commands: list[click.Command] = []
30
+ _discovered = False
31
+
32
+
33
+ def command(name: str | None = None, **kwargs) -> Callable:
34
+ """
35
+ Decorator to register a custom command.
36
+
37
+ Args:
38
+ name: Command name (defaults to function name with underscores replaced by hyphens)
39
+ **kwargs: Additional arguments passed to click.command()
40
+
41
+ Example:
42
+ @command("seed", help="Seed database with initial data")
43
+ @click.option("--count", "-c", default=10)
44
+ def seed_data(count: int):
45
+ click.echo(f"Seeding {count} records...")
46
+ """
47
+
48
+ def decorator(func: Callable) -> click.Command:
49
+ cmd_name = name or func.__name__.replace("_", "-")
50
+ cmd = click.command(cmd_name, **kwargs)(func)
51
+ _commands.append(cmd)
52
+ return cmd
53
+
54
+ return decorator
55
+
56
+
57
+ def discover_commands() -> list[click.Command]:
58
+ """
59
+ Auto-discover all commands in this package.
60
+
61
+ Imports all modules in the app.commands package (except those starting with _)
62
+ which triggers the @command decorator to register them.
63
+
64
+ Returns:
65
+ List of discovered click.Command objects
66
+ """
67
+ global _discovered
68
+
69
+ if _discovered:
70
+ return _commands
71
+
72
+ package_dir = Path(__file__).parent
73
+
74
+ for _, module_name, _ in pkgutil.iter_modules([str(package_dir)]):
75
+ if module_name.startswith("_"):
76
+ continue
77
+
78
+ try:
79
+ importlib.import_module(f"app.commands.{module_name}")
80
+ except ImportError as e:
81
+ click.secho(f"Warning: Failed to import command module '{module_name}': {e}", fg="yellow")
82
+
83
+ _discovered = True
84
+ return _commands
85
+
86
+
87
+ def register_commands(cli: click.Group) -> None:
88
+ """
89
+ Register all discovered commands to a CLI group.
90
+
91
+ Args:
92
+ cli: The click.Group to add commands to
93
+ """
94
+ commands = discover_commands()
95
+
96
+ for cmd in commands:
97
+ cli.add_command(cmd)
98
+
99
+
100
+ def success(message: str) -> None:
101
+ """Print success message in green."""
102
+ click.secho(message, fg="green")
103
+
104
+
105
+ def error(message: str) -> None:
106
+ """Print error message in red."""
107
+ click.secho(message, fg="red")
108
+
109
+
110
+ def warning(message: str) -> None:
111
+ """Print warning message in yellow."""
112
+ click.secho(message, fg="yellow")
113
+
114
+
115
+ def info(message: str) -> None:
116
+ """Print info message."""
117
+ click.echo(message)
@@ -0,0 +1,75 @@
1
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite -%}
2
+ """
3
+ Cleanup old or stale data from the database.
4
+
5
+ This command is useful for maintenance tasks.
6
+ """
7
+
8
+ import asyncio
9
+ from datetime import datetime, timedelta
10
+
11
+ import click
12
+
13
+ from app.commands import command, info, success, warning
14
+
15
+
16
+ @command("cleanup", help="Clean up old data from the database")
17
+ @click.option("--days", "-d", default=30, type=int, help="Delete records older than N days")
18
+ @click.option("--dry-run", is_flag=True, help="Show what would be deleted without making changes")
19
+ @click.option("--force", "-f", is_flag=True, help="Skip confirmation prompt")
20
+ def cleanup(days: int, dry_run: bool, force: bool) -> None:
21
+ """
22
+ Remove old records from the database.
23
+
24
+ Example:
25
+ project cmd cleanup --days 90
26
+ project cmd cleanup --days 30 --dry-run
27
+ project cmd cleanup --days 7 --force
28
+ """
29
+ cutoff_date = datetime.utcnow() - timedelta(days=days)
30
+
31
+ if dry_run:
32
+ info(f"[DRY RUN] Would delete records older than {cutoff_date}")
33
+ return
34
+
35
+ if not force and not click.confirm(f"Delete all records older than {days} days ({cutoff_date})?"):
36
+ warning("Aborted.")
37
+ return
38
+
39
+ {%- if cookiecutter.use_postgresql %}
40
+ from app.db.session import async_session_maker
41
+
42
+ async def _cleanup():
43
+ async with async_session_maker() as _session:
44
+ info(f"Cleaning up records older than {cutoff_date}...")
45
+
46
+ # Add your cleanup logic here
47
+ # Example:
48
+ # result = await session.execute(
49
+ # delete(YourModel).where(YourModel.created_at < cutoff_date)
50
+ # )
51
+ # await session.commit()
52
+ # deleted_count = result.rowcount
53
+
54
+ deleted_count = 0 # Replace with actual count
55
+ success(f"Deleted {deleted_count} records.")
56
+
57
+ asyncio.run(_cleanup())
58
+ {%- elif cookiecutter.use_sqlite %}
59
+ from app.db.session import SessionLocal
60
+
61
+ with SessionLocal() as _session:
62
+ info(f"Cleaning up records older than {cutoff_date}...")
63
+
64
+ # Add your cleanup logic here
65
+ # Example:
66
+ # result = session.execute(
67
+ # delete(YourModel).where(YourModel.created_at < cutoff_date)
68
+ # )
69
+ # session.commit()
70
+ # deleted_count = result.rowcount
71
+
72
+ deleted_count = 0 # Replace with actual count
73
+ success(f"Deleted {deleted_count} records.")
74
+ {%- endif %}
75
+ {%- endif %}
@@ -0,0 +1,28 @@
1
+ """
2
+ Example custom command.
3
+
4
+ This is a template showing how to create custom CLI commands.
5
+ Copy this file and modify it to create your own commands.
6
+ """
7
+
8
+ import click
9
+
10
+ from app.commands import command, info, success
11
+
12
+
13
+ @command("hello", help="Example command that greets the user")
14
+ @click.option("--name", "-n", default="World", help="Name to greet")
15
+ @click.option("--count", "-c", default=1, type=int, help="Number of greetings")
16
+ def hello(name: str, count: int) -> None:
17
+ """
18
+ Greet someone multiple times.
19
+
20
+ Example:
21
+ project cmd hello --name Alice --count 3
22
+ """
23
+ info(f"Greeting {name} {count} time(s)...")
24
+
25
+ for i in range(count):
26
+ click.echo(f" [{i + 1}] Hello, {name}!")
27
+
28
+ success("Done!")