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,334 @@
1
+ """FastAPI application entry point."""
2
+
3
+ from collections.abc import AsyncGenerator
4
+ from contextlib import asynccontextmanager
5
+ {%- if cookiecutter.enable_redis %}
6
+ from typing import TypedDict
7
+ {%- endif %}
8
+
9
+ from fastapi import FastAPI
10
+ {%- if cookiecutter.enable_orjson %}
11
+ from fastapi.responses import ORJSONResponse
12
+ {%- endif %}
13
+ {%- if cookiecutter.enable_pagination %}
14
+ from fastapi_pagination import add_pagination
15
+ {%- endif %}
16
+
17
+ from app.api.exception_handlers import register_exception_handlers
18
+ from app.api.router import api_router
19
+ from app.core.config import settings
20
+ from app.core.logfire_setup import instrument_app, setup_logfire
21
+ from app.core.middleware import RequestIDMiddleware
22
+
23
+ {%- if cookiecutter.enable_redis %}
24
+ from app.clients.redis import RedisClient
25
+
26
+
27
+ class LifespanState(TypedDict):
28
+ """Lifespan state - resources available via request.state."""
29
+
30
+ redis: RedisClient
31
+ {%- endif %}
32
+
33
+
34
+ @asynccontextmanager
35
+ async def lifespan(app: FastAPI) -> AsyncGenerator[{% if cookiecutter.enable_redis %}LifespanState{% else %}None{% endif %}, None]:
36
+ """Application lifespan - startup and shutdown events.
37
+
38
+ Resources yielded here are available via request.state in route handlers.
39
+ See: https://asgi.readthedocs.io/en/latest/specs/lifespan.html#lifespan-state
40
+ """
41
+ # === Startup ===
42
+ setup_logfire()
43
+
44
+ {%- if cookiecutter.use_postgresql and cookiecutter.enable_logfire and cookiecutter.logfire_database %}
45
+ from app.core.logfire_setup import instrument_asyncpg
46
+ instrument_asyncpg()
47
+ {%- endif %}
48
+
49
+ {%- if cookiecutter.use_mongodb and cookiecutter.enable_logfire and cookiecutter.logfire_database %}
50
+ from app.core.logfire_setup import instrument_pymongo
51
+ instrument_pymongo()
52
+ {%- endif %}
53
+
54
+ {%- if cookiecutter.enable_redis and cookiecutter.enable_logfire and cookiecutter.logfire_redis %}
55
+ from app.core.logfire_setup import instrument_redis
56
+ instrument_redis()
57
+ {%- endif %}
58
+
59
+ {%- if cookiecutter.enable_logfire and cookiecutter.logfire_httpx %}
60
+ from app.core.logfire_setup import instrument_httpx
61
+ instrument_httpx()
62
+ {%- endif %}
63
+
64
+ {%- if cookiecutter.enable_logfire and cookiecutter.enable_ai_agent and cookiecutter.use_pydantic_ai %}
65
+ from app.core.logfire_setup import instrument_pydantic_ai
66
+ instrument_pydantic_ai()
67
+ {%- endif %}
68
+
69
+ {%- if cookiecutter.enable_redis %}
70
+ redis_client = RedisClient()
71
+ await redis_client.connect()
72
+ {%- endif %}
73
+
74
+ {%- if cookiecutter.enable_caching %}
75
+ from app.core.cache import setup_cache
76
+ setup_cache(redis_client)
77
+ {%- endif %}
78
+
79
+ {%- if cookiecutter.enable_redis %}
80
+
81
+ yield {"redis": redis_client}
82
+
83
+ # === Shutdown ===
84
+ await redis_client.close()
85
+ {%- else %}
86
+
87
+ yield
88
+
89
+ # === Shutdown ===
90
+ {%- endif %}
91
+ {%- if cookiecutter.use_postgresql %}
92
+ from app.db.session import close_db
93
+ await close_db()
94
+ {%- endif %}
95
+
96
+ {%- if cookiecutter.use_mongodb %}
97
+ from app.db.session import close_db
98
+ await close_db()
99
+ {%- endif %}
100
+
101
+ {%- if cookiecutter.use_sqlite %}
102
+ from app.db.session import close_db
103
+ close_db()
104
+ {%- endif %}
105
+
106
+
107
+ # Environments where API docs should be visible
108
+ SHOW_DOCS_ENVIRONMENTS = ("local", "staging", "development")
109
+
110
+
111
+ def create_app() -> FastAPI:
112
+ """Create and configure the FastAPI application."""
113
+ # Only show docs in allowed environments (hide in production)
114
+ show_docs = settings.ENVIRONMENT in SHOW_DOCS_ENVIRONMENTS
115
+ openapi_url = f"{settings.API_V1_STR}/openapi.json" if show_docs else None
116
+ docs_url = "/docs" if show_docs else None
117
+ redoc_url = "/redoc" if show_docs else None
118
+
119
+ # OpenAPI tags for better documentation organization
120
+ openapi_tags = [
121
+ {
122
+ "name": "health",
123
+ "description": "Health check endpoints for monitoring and Kubernetes probes",
124
+ },
125
+ {%- if cookiecutter.use_jwt %}
126
+ {
127
+ "name": "auth",
128
+ "description": "Authentication endpoints - login, register, token refresh",
129
+ },
130
+ {
131
+ "name": "users",
132
+ "description": "User management endpoints",
133
+ },
134
+ {%- endif %}
135
+ {%- if cookiecutter.enable_oauth %}
136
+ {
137
+ "name": "oauth",
138
+ "description": "OAuth2 social login endpoints (Google, etc.)",
139
+ },
140
+ {%- endif %}
141
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
142
+ {
143
+ "name": "sessions",
144
+ "description": "Session management - view and manage active login sessions",
145
+ },
146
+ {%- endif %}
147
+ {%- if cookiecutter.include_example_crud %}
148
+ {
149
+ "name": "items",
150
+ "description": "Example CRUD endpoints demonstrating the API pattern",
151
+ },
152
+ {%- endif %}
153
+ {%- if cookiecutter.enable_conversation_persistence %}
154
+ {
155
+ "name": "conversations",
156
+ "description": "AI conversation persistence - manage chat history",
157
+ },
158
+ {%- endif %}
159
+ {%- if cookiecutter.enable_webhooks %}
160
+ {
161
+ "name": "webhooks",
162
+ "description": "Webhook management - subscribe to events and manage deliveries",
163
+ },
164
+ {%- endif %}
165
+ {%- if cookiecutter.enable_ai_agent %}
166
+ {
167
+ "name": "agent",
168
+ "description": "AI agent WebSocket endpoint for real-time chat",
169
+ },
170
+ {%- endif %}
171
+ {%- if cookiecutter.enable_websockets %}
172
+ {
173
+ "name": "websocket",
174
+ "description": "WebSocket endpoints for real-time communication",
175
+ },
176
+ {%- endif %}
177
+ ]
178
+
179
+ app = FastAPI(
180
+ title=settings.PROJECT_NAME,
181
+ summary="FastAPI application with Logfire observability",
182
+ description="""
183
+ {{ cookiecutter.project_description }}
184
+
185
+ ## Features
186
+
187
+ {%- if cookiecutter.use_jwt %}
188
+ - **Authentication**: JWT-based authentication with refresh tokens
189
+ {%- endif %}
190
+ {%- if cookiecutter.use_api_key %}
191
+ - **API Key**: Header-based API key authentication
192
+ {%- endif %}
193
+ {%- if cookiecutter.use_database %}
194
+ - **Database**: Async database operations
195
+ {%- endif %}
196
+ {%- if cookiecutter.enable_redis %}
197
+ - **Redis**: Caching and session storage
198
+ {%- endif %}
199
+ {%- if cookiecutter.enable_rate_limiting %}
200
+ - **Rate Limiting**: Request rate limiting per client
201
+ {%- endif %}
202
+ {%- if cookiecutter.enable_ai_agent and cookiecutter.use_pydantic_ai %}
203
+ - **AI Agent**: PydanticAI-powered conversational assistant
204
+ {%- endif %}
205
+ {%- if cookiecutter.enable_ai_agent and cookiecutter.use_langchain %}
206
+ - **AI Agent**: LangChain-powered conversational assistant
207
+ {%- endif %}
208
+ {%- if cookiecutter.enable_logfire %}
209
+ - **Observability**: Logfire integration for tracing and monitoring
210
+ {%- endif %}
211
+
212
+ ## Documentation
213
+
214
+ - [Swagger UI](/docs) - Interactive API documentation
215
+ - [ReDoc](/redoc) - Alternative documentation view
216
+ """.strip(),
217
+ version="0.1.0",
218
+ openapi_url=openapi_url,
219
+ docs_url=docs_url,
220
+ redoc_url=redoc_url,
221
+ openapi_tags=openapi_tags,
222
+ contact={
223
+ "name": "{{ cookiecutter.author_name }}",
224
+ "email": "{{ cookiecutter.author_email }}",
225
+ },
226
+ license_info={
227
+ "name": "MIT",
228
+ "identifier": "MIT",
229
+ },
230
+ lifespan=lifespan,
231
+ {%- if cookiecutter.enable_orjson %}
232
+ default_response_class=ORJSONResponse,
233
+ {%- endif %}
234
+ )
235
+
236
+ # Logfire instrumentation
237
+ instrument_app(app)
238
+
239
+ # Request ID middleware (for request correlation/debugging)
240
+ app.add_middleware(RequestIDMiddleware)
241
+
242
+ # Exception handlers
243
+ register_exception_handlers(app)
244
+
245
+ {%- if cookiecutter.enable_cors %}
246
+
247
+ # CORS middleware
248
+ from starlette.middleware.cors import CORSMiddleware
249
+ app.add_middleware(
250
+ CORSMiddleware,
251
+ allow_origins=settings.CORS_ORIGINS,
252
+ allow_credentials=settings.CORS_ALLOW_CREDENTIALS,
253
+ allow_methods=settings.CORS_ALLOW_METHODS,
254
+ allow_headers=settings.CORS_ALLOW_HEADERS,
255
+ )
256
+ {%- endif %}
257
+
258
+ {%- if cookiecutter.enable_sentry %}
259
+
260
+ # Sentry
261
+ if settings.SENTRY_DSN:
262
+ import sentry_sdk
263
+ sentry_sdk.init(dsn=settings.SENTRY_DSN, enable_tracing=True)
264
+ {%- endif %}
265
+
266
+ {%- if cookiecutter.enable_rate_limiting %}
267
+
268
+ # Rate limiting
269
+ # Note: slowapi requires app.state.limiter - this is a library requirement,
270
+ # not suitable for lifespan state pattern which is for request-scoped access
271
+ from app.core.rate_limit import limiter
272
+ from slowapi import _rate_limit_exceeded_handler
273
+ from slowapi.errors import RateLimitExceeded
274
+ app.state.limiter = limiter
275
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
276
+ {%- endif %}
277
+
278
+ {%- if (cookiecutter.enable_admin_panel and cookiecutter.use_postgresql and cookiecutter.admin_require_auth and not cookiecutter.admin_env_disabled) or cookiecutter.enable_oauth %}
279
+
280
+ # Session middleware (for admin authentication and/or OAuth)
281
+ from starlette.middleware.sessions import SessionMiddleware
282
+ app.add_middleware(SessionMiddleware, secret_key=settings.SECRET_KEY)
283
+ {%- endif %}
284
+
285
+ {%- if cookiecutter.enable_admin_panel and cookiecutter.use_postgresql %}
286
+ {%- if cookiecutter.admin_env_disabled %}
287
+ # Admin panel - disabled
288
+ {%- elif cookiecutter.admin_env_all %}
289
+
290
+ # Admin panel (all environments)
291
+ from app.admin import setup_admin
292
+ setup_admin(app)
293
+ {%- else %}
294
+
295
+ # Admin panel (environment restricted)
296
+ {%- if cookiecutter.admin_env_dev_only %}
297
+ ADMIN_ALLOWED_ENVIRONMENTS = ["development", "local"]
298
+ {%- elif cookiecutter.admin_env_dev_staging %}
299
+ ADMIN_ALLOWED_ENVIRONMENTS = ["development", "local", "staging"]
300
+ {%- endif %}
301
+
302
+ if settings.ENVIRONMENT in ADMIN_ALLOWED_ENVIRONMENTS:
303
+ from app.admin import setup_admin
304
+ setup_admin(app)
305
+ {%- endif %}
306
+ {%- endif %}
307
+
308
+ # API Version Deprecation (uncomment when deprecating old versions)
309
+ # Example: Mark v1 as deprecated when v2 is ready
310
+ # from app.api.versioning import VersionDeprecationMiddleware
311
+ # app.add_middleware(
312
+ # VersionDeprecationMiddleware,
313
+ # deprecated_versions={
314
+ # "v1": {
315
+ # "sunset": "2025-12-31",
316
+ # "link": "/docs/migration/v2",
317
+ # "message": "Please migrate to API v2",
318
+ # }
319
+ # },
320
+ # )
321
+
322
+ # Include API router
323
+ app.include_router(api_router, prefix=settings.API_V1_STR)
324
+
325
+ {%- if cookiecutter.enable_pagination %}
326
+
327
+ # Pagination
328
+ add_pagination(app)
329
+ {%- endif %}
330
+
331
+ return app
332
+
333
+
334
+ app = create_app()
@@ -0,0 +1,9 @@
1
+ """Background processing pipelines.
2
+
3
+ This module contains ETL pipelines, data processing workflows,
4
+ and batch operations that run as background tasks.
5
+ """
6
+
7
+ from app.pipelines.base import BasePipeline, PipelineResult
8
+
9
+ __all__ = ["BasePipeline", "PipelineResult"]
@@ -0,0 +1,73 @@
1
+ """Base pipeline classes."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass, field
5
+
6
+
7
+ @dataclass
8
+ class PipelineResult:
9
+ """Result of a pipeline execution.
10
+
11
+ Attributes:
12
+ processed: Number of items successfully processed.
13
+ failed: Number of items that failed processing.
14
+ errors: List of error messages for failed items.
15
+ metadata: Additional metadata about the pipeline run.
16
+ """
17
+
18
+ processed: int
19
+ failed: int = 0
20
+ errors: list[str] = field(default_factory=list)
21
+ metadata: dict = field(default_factory=dict)
22
+
23
+ @property
24
+ def success_rate(self) -> float:
25
+ """Calculate success rate as a percentage."""
26
+ total = self.processed + self.failed
27
+ if total == 0:
28
+ return 100.0
29
+ return (self.processed / total) * 100
30
+
31
+ @property
32
+ def has_errors(self) -> bool:
33
+ """Check if any errors occurred."""
34
+ return self.failed > 0 or len(self.errors) > 0
35
+
36
+
37
+ class BasePipeline(ABC):
38
+ """Base class for all pipelines.
39
+
40
+ Pipelines are used for background processing tasks like:
41
+ - ETL operations
42
+ - Batch data processing
43
+ - Embedding generation
44
+ - Data synchronization
45
+
46
+ Subclasses must implement the `run` method.
47
+ """
48
+
49
+ @abstractmethod
50
+ async def run(self) -> PipelineResult:
51
+ """Execute the pipeline.
52
+
53
+ Returns:
54
+ PipelineResult with processing statistics.
55
+ """
56
+ pass
57
+
58
+ async def validate(self) -> bool:
59
+ """Validate pipeline configuration before running.
60
+
61
+ Override this method to add custom validation logic.
62
+
63
+ Returns:
64
+ True if validation passes, False otherwise.
65
+ """
66
+ return True
67
+
68
+ async def cleanup(self) -> None: # noqa: B027
69
+ """Cleanup resources after pipeline execution.
70
+
71
+ Override this method to add custom cleanup logic.
72
+ Default implementation does nothing.
73
+ """
@@ -0,0 +1,49 @@
1
+ """Repository layer for database operations."""
2
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite or cookiecutter.use_jwt or cookiecutter.include_example_crud or cookiecutter.enable_conversation_persistence or cookiecutter.enable_webhooks %}
3
+ # ruff: noqa: I001, RUF022 - Imports structured for Jinja2 template conditionals
4
+ {%- endif %}
5
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
6
+
7
+ from app.repositories.base import BaseRepository
8
+ {%- endif %}
9
+ {%- if cookiecutter.use_jwt %}
10
+
11
+ from app.repositories import user as user_repo
12
+ {%- endif %}
13
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
14
+
15
+ from app.repositories import session as session_repo
16
+ {%- endif %}
17
+ {%- if cookiecutter.include_example_crud and cookiecutter.use_database %}
18
+
19
+ from app.repositories import item as item_repo
20
+ {%- endif %}
21
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
22
+
23
+ from app.repositories import conversation as conversation_repo
24
+ {%- endif %}
25
+ {%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
26
+
27
+ from app.repositories import webhook as webhook_repo
28
+ {%- endif %}
29
+
30
+ __all__ = [
31
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
32
+ "BaseRepository",
33
+ {%- endif %}
34
+ {%- if cookiecutter.use_jwt %}
35
+ "user_repo",
36
+ {%- endif %}
37
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
38
+ "session_repo",
39
+ {%- endif %}
40
+ {%- if cookiecutter.include_example_crud and cookiecutter.use_database %}
41
+ "item_repo",
42
+ {%- endif %}
43
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
44
+ "conversation_repo",
45
+ {%- endif %}
46
+ {%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
47
+ "webhook_repo",
48
+ {%- endif %}
49
+ ]
@@ -0,0 +1,154 @@
1
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
2
+ """Base repository with generic CRUD operations."""
3
+
4
+ from typing import Any, Generic, TypeVar
5
+
6
+ from pydantic import BaseModel
7
+ from sqlalchemy import select
8
+ {%- if cookiecutter.use_postgresql %}
9
+ from sqlalchemy.ext.asyncio import AsyncSession
10
+ {%- else %}
11
+ from sqlalchemy.orm import Session
12
+ {%- endif %}
13
+
14
+ from app.db.base import Base
15
+
16
+ ModelType = TypeVar("ModelType", bound=Base)
17
+ CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
18
+ UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)
19
+
20
+
21
+ class BaseRepository(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
22
+ """Base class for repository operations.
23
+
24
+ Provides generic CRUD operations for SQLAlchemy models.
25
+ Subclasses should specify the model type via the model attribute.
26
+ """
27
+
28
+ def __init__(self, model: type[ModelType]):
29
+ self.model = model
30
+
31
+ {%- if cookiecutter.use_postgresql %}
32
+
33
+ async def get(self, db: AsyncSession, id: Any) -> ModelType | None:
34
+ """Get a single record by ID."""
35
+ return await db.get(self.model, id)
36
+
37
+ async def get_multi(
38
+ self,
39
+ db: AsyncSession,
40
+ *,
41
+ skip: int = 0,
42
+ limit: int = 100,
43
+ ) -> list[ModelType]:
44
+ """Get multiple records with pagination."""
45
+ result = await db.execute(
46
+ select(self.model).offset(skip).limit(limit)
47
+ )
48
+ return list(result.scalars().all())
49
+
50
+ async def create(
51
+ self,
52
+ db: AsyncSession,
53
+ *,
54
+ obj_in: CreateSchemaType,
55
+ ) -> ModelType:
56
+ """Create a new record."""
57
+ obj_in_data = obj_in.model_dump()
58
+ db_obj = self.model(**obj_in_data)
59
+ db.add(db_obj)
60
+ await db.flush()
61
+ await db.refresh(db_obj)
62
+ return db_obj
63
+
64
+ async def update(
65
+ self,
66
+ db: AsyncSession,
67
+ *,
68
+ db_obj: ModelType,
69
+ obj_in: UpdateSchemaType | dict[str, Any],
70
+ ) -> ModelType:
71
+ """Update a record."""
72
+ update_data = obj_in if isinstance(obj_in, dict) else obj_in.model_dump(exclude_unset=True)
73
+
74
+ for field, value in update_data.items():
75
+ setattr(db_obj, field, value)
76
+
77
+ db.add(db_obj)
78
+ await db.flush()
79
+ await db.refresh(db_obj)
80
+ return db_obj
81
+
82
+ async def delete(self, db: AsyncSession, *, id: Any) -> ModelType | None:
83
+ """Delete a record."""
84
+ obj = await self.get(db, id)
85
+ if obj:
86
+ await db.delete(obj)
87
+ await db.flush()
88
+ return obj
89
+
90
+ {%- else %}
91
+
92
+ def get(self, db: Session, id: Any) -> ModelType | None:
93
+ """Get a single record by ID."""
94
+ return db.get(self.model, id)
95
+
96
+ def get_multi(
97
+ self,
98
+ db: Session,
99
+ *,
100
+ skip: int = 0,
101
+ limit: int = 100,
102
+ ) -> list[ModelType]:
103
+ """Get multiple records with pagination."""
104
+ result = db.execute(
105
+ select(self.model).offset(skip).limit(limit)
106
+ )
107
+ return list(result.scalars().all())
108
+
109
+ def create(
110
+ self,
111
+ db: Session,
112
+ *,
113
+ obj_in: CreateSchemaType,
114
+ ) -> ModelType:
115
+ """Create a new record."""
116
+ obj_in_data = obj_in.model_dump()
117
+ db_obj = self.model(**obj_in_data)
118
+ db.add(db_obj)
119
+ db.flush()
120
+ db.refresh(db_obj)
121
+ return db_obj
122
+
123
+ def update(
124
+ self,
125
+ db: Session,
126
+ *,
127
+ db_obj: ModelType,
128
+ obj_in: UpdateSchemaType | dict[str, Any],
129
+ ) -> ModelType:
130
+ """Update a record."""
131
+ if isinstance(obj_in, dict):
132
+ update_data = obj_in
133
+ else:
134
+ update_data = obj_in.model_dump(exclude_unset=True)
135
+
136
+ for field, value in update_data.items():
137
+ setattr(db_obj, field, value)
138
+
139
+ db.add(db_obj)
140
+ db.flush()
141
+ db.refresh(db_obj)
142
+ return db_obj
143
+
144
+ def delete(self, db: Session, *, id: Any) -> ModelType | None:
145
+ """Delete a record."""
146
+ obj = self.get(db, id)
147
+ if obj:
148
+ db.delete(obj)
149
+ db.flush()
150
+ return obj
151
+ {%- endif %}
152
+ {%- else %}
153
+ """Base repository - not using SQLAlchemy."""
154
+ {%- endif %}