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,218 @@
1
+ {%- if cookiecutter.use_jwt and cookiecutter.use_postgresql %}
2
+ """User database model."""
3
+
4
+ import uuid
5
+ from enum import Enum
6
+ {%- if cookiecutter.enable_session_management %}
7
+ from typing import TYPE_CHECKING
8
+ {%- endif %}
9
+
10
+ from sqlalchemy import Boolean, String
11
+ from sqlalchemy.dialects.postgresql import UUID
12
+ from sqlalchemy.orm import Mapped, mapped_column{% if cookiecutter.enable_session_management %}, relationship{% endif %}
13
+
14
+ from app.db.base import Base, TimestampMixin
15
+
16
+ {%- if cookiecutter.enable_session_management %}
17
+ if TYPE_CHECKING:
18
+ from app.db.models.session import Session
19
+ {%- endif %}
20
+
21
+
22
+ class UserRole(str, Enum):
23
+ """User role enumeration.
24
+
25
+ Roles hierarchy (higher includes lower permissions):
26
+ - ADMIN: Full system access, can manage users and settings
27
+ - USER: Standard user access
28
+ """
29
+
30
+ ADMIN = "admin"
31
+ USER = "user"
32
+
33
+
34
+ class User(Base, TimestampMixin):
35
+ """User model."""
36
+
37
+ __tablename__ = "users"
38
+
39
+ id: Mapped[uuid.UUID] = mapped_column(
40
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
41
+ )
42
+ email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
43
+ hashed_password: Mapped[str | None] = mapped_column(String(255), nullable=True)
44
+ full_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
45
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
46
+ is_superuser: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
47
+ role: Mapped[str] = mapped_column(String(50), default=UserRole.USER.value, nullable=False)
48
+ {%- if cookiecutter.enable_oauth %}
49
+ oauth_provider: Mapped[str | None] = mapped_column(String(50), nullable=True, index=True)
50
+ oauth_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
51
+ {%- endif %}
52
+
53
+ {%- if cookiecutter.enable_session_management %}
54
+
55
+ # Relationship to sessions
56
+ sessions: Mapped[list["Session"]] = relationship(
57
+ "Session", back_populates="user", cascade="all, delete-orphan"
58
+ )
59
+ {%- endif %}
60
+
61
+ @property
62
+ def user_role(self) -> UserRole:
63
+ """Get role as enum."""
64
+ return UserRole(self.role)
65
+
66
+ def has_role(self, required_role: UserRole) -> bool:
67
+ """Check if user has the required role or higher.
68
+
69
+ Admin role has access to everything.
70
+ """
71
+ if self.role == UserRole.ADMIN.value:
72
+ return True
73
+ return self.role == required_role.value
74
+
75
+ def __repr__(self) -> str:
76
+ return f"<User(id={self.id}, email={self.email}, role={self.role})>"
77
+
78
+
79
+ {%- elif cookiecutter.use_jwt and cookiecutter.use_sqlite %}
80
+ """User database model."""
81
+
82
+ import uuid
83
+ from enum import Enum
84
+ {%- if cookiecutter.enable_session_management %}
85
+ from typing import TYPE_CHECKING
86
+ {%- endif %}
87
+
88
+ from sqlalchemy import Boolean, String
89
+ from sqlalchemy.orm import Mapped, mapped_column{% if cookiecutter.enable_session_management %}, relationship{% endif %}
90
+
91
+ from app.db.base import Base, TimestampMixin
92
+
93
+ {%- if cookiecutter.enable_session_management %}
94
+ if TYPE_CHECKING:
95
+ from app.db.models.session import Session
96
+ {%- endif %}
97
+
98
+
99
+ class UserRole(str, Enum):
100
+ """User role enumeration.
101
+
102
+ Roles hierarchy (higher includes lower permissions):
103
+ - ADMIN: Full system access, can manage users and settings
104
+ - USER: Standard user access
105
+ """
106
+
107
+ ADMIN = "admin"
108
+ USER = "user"
109
+
110
+
111
+ class User(Base, TimestampMixin):
112
+ """User model."""
113
+
114
+ __tablename__ = "users"
115
+
116
+ id: Mapped[str] = mapped_column(
117
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
118
+ )
119
+ email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
120
+ hashed_password: Mapped[str | None] = mapped_column(String(255), nullable=True)
121
+ full_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
122
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
123
+ is_superuser: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
124
+ role: Mapped[str] = mapped_column(String(50), default=UserRole.USER.value, nullable=False)
125
+ {%- if cookiecutter.enable_oauth %}
126
+ oauth_provider: Mapped[str | None] = mapped_column(String(50), nullable=True, index=True)
127
+ oauth_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
128
+ {%- endif %}
129
+
130
+ {%- if cookiecutter.enable_session_management %}
131
+
132
+ # Relationship to sessions
133
+ sessions: Mapped[list["Session"]] = relationship(
134
+ "Session", back_populates="user", cascade="all, delete-orphan"
135
+ )
136
+ {%- endif %}
137
+
138
+ @property
139
+ def user_role(self) -> UserRole:
140
+ """Get role as enum."""
141
+ return UserRole(self.role)
142
+
143
+ def has_role(self, required_role: UserRole) -> bool:
144
+ """Check if user has the required role or higher.
145
+
146
+ Admin role has access to everything.
147
+ """
148
+ if self.role == UserRole.ADMIN.value:
149
+ return True
150
+ return self.role == required_role.value
151
+
152
+ def __repr__(self) -> str:
153
+ return f"<User(id={self.id}, email={self.email}, role={self.role})>"
154
+
155
+
156
+ {%- elif cookiecutter.use_jwt and cookiecutter.use_mongodb %}
157
+ """User document model for MongoDB."""
158
+
159
+ from datetime import UTC, datetime
160
+ from enum import Enum
161
+ from typing import Optional
162
+
163
+ from beanie import Document
164
+ from pydantic import EmailStr, Field
165
+
166
+
167
+ class UserRole(str, Enum):
168
+ """User role enumeration.
169
+
170
+ Roles hierarchy (higher includes lower permissions):
171
+ - ADMIN: Full system access, can manage users and settings
172
+ - USER: Standard user access
173
+ """
174
+
175
+ ADMIN = "admin"
176
+ USER = "user"
177
+
178
+
179
+ class User(Document):
180
+ """User document model."""
181
+
182
+ email: EmailStr
183
+ hashed_password: Optional[str] = None
184
+ full_name: Optional[str] = None
185
+ is_active: bool = True
186
+ is_superuser: bool = False
187
+ role: str = UserRole.USER.value
188
+ {%- if cookiecutter.enable_oauth %}
189
+ oauth_provider: Optional[str] = None
190
+ oauth_id: Optional[str] = None
191
+ {%- endif %}
192
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
193
+ updated_at: Optional[datetime] = None
194
+
195
+ @property
196
+ def user_role(self) -> UserRole:
197
+ """Get role as enum."""
198
+ return UserRole(self.role)
199
+
200
+ def has_role(self, required_role: UserRole) -> bool:
201
+ """Check if user has the required role or higher.
202
+
203
+ Admin role has access to everything.
204
+ """
205
+ if self.role == UserRole.ADMIN.value:
206
+ return True
207
+ return self.role == required_role.value
208
+
209
+ class Settings:
210
+ name = "users"
211
+ indexes = [
212
+ "email",
213
+ ]
214
+
215
+
216
+ {%- else %}
217
+ """User model - not configured."""
218
+ {%- endif %}
@@ -0,0 +1,244 @@
1
+ {%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
2
+ {%- if cookiecutter.use_postgresql %}
3
+ """Webhook database models (PostgreSQL async)."""
4
+
5
+ import uuid
6
+ from datetime import datetime
7
+ from enum import Enum
8
+
9
+ from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
10
+ from sqlalchemy.dialects.postgresql import ARRAY, UUID
11
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
12
+
13
+ from app.db.base import Base, TimestampMixin
14
+
15
+
16
+ class WebhookEventType(str, Enum):
17
+ """Webhook event types."""
18
+
19
+ # User events
20
+ USER_CREATED = "user.created"
21
+ USER_UPDATED = "user.updated"
22
+ USER_DELETED = "user.deleted"
23
+
24
+ # Custom events (extend as needed)
25
+ ITEM_CREATED = "item.created"
26
+ ITEM_UPDATED = "item.updated"
27
+ ITEM_DELETED = "item.deleted"
28
+
29
+
30
+ class Webhook(Base, TimestampMixin):
31
+ """Webhook subscription model."""
32
+
33
+ __tablename__ = "webhooks"
34
+
35
+ id: Mapped[uuid.UUID] = mapped_column(
36
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
37
+ )
38
+ name: Mapped[str] = mapped_column(String(255), nullable=False)
39
+ url: Mapped[str] = mapped_column(String(2048), nullable=False)
40
+ secret: Mapped[str] = mapped_column(String(255), nullable=False)
41
+ events: Mapped[list[str]] = mapped_column(ARRAY(String), nullable=False)
42
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
43
+ description: Mapped[str | None] = mapped_column(Text, nullable=True)
44
+
45
+ # Optional: Associate webhook with a user
46
+ {%- if cookiecutter.use_jwt %}
47
+ user_id: Mapped[uuid.UUID | None] = mapped_column(
48
+ UUID(as_uuid=True), ForeignKey("users.id"), nullable=True
49
+ )
50
+ {%- endif %}
51
+
52
+ # Relationship to delivery logs
53
+ deliveries: Mapped[list["WebhookDelivery"]] = relationship(
54
+ "WebhookDelivery", back_populates="webhook", cascade="all, delete-orphan"
55
+ )
56
+
57
+
58
+ class WebhookDelivery(Base):
59
+ """Webhook delivery log model."""
60
+
61
+ __tablename__ = "webhook_deliveries"
62
+
63
+ id: Mapped[uuid.UUID] = mapped_column(
64
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
65
+ )
66
+ webhook_id: Mapped[uuid.UUID] = mapped_column(
67
+ UUID(as_uuid=True), ForeignKey("webhooks.id"), nullable=False
68
+ )
69
+ event_type: Mapped[str] = mapped_column(String(100), nullable=False)
70
+ payload: Mapped[str] = mapped_column(Text, nullable=False)
71
+ response_status: Mapped[int | None] = mapped_column(Integer, nullable=True)
72
+ response_body: Mapped[str | None] = mapped_column(Text, nullable=True)
73
+ error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
74
+ attempt_count: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
75
+ success: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
76
+ created_at: Mapped[datetime] = mapped_column(
77
+ DateTime, default=datetime.utcnow, nullable=False
78
+ )
79
+ delivered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
80
+
81
+ # Relationship
82
+ webhook: Mapped["Webhook"] = relationship("Webhook", back_populates="deliveries")
83
+
84
+
85
+ {%- elif cookiecutter.use_sqlite %}
86
+ """Webhook database models (SQLite sync)."""
87
+
88
+ import uuid
89
+ from datetime import datetime
90
+ from enum import Enum
91
+
92
+ from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
93
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
94
+
95
+ from app.db.base import Base, TimestampMixin
96
+
97
+
98
+ class WebhookEventType(str, Enum):
99
+ """Webhook event types."""
100
+
101
+ # User events
102
+ USER_CREATED = "user.created"
103
+ USER_UPDATED = "user.updated"
104
+ USER_DELETED = "user.deleted"
105
+
106
+ # Custom events (extend as needed)
107
+ ITEM_CREATED = "item.created"
108
+ ITEM_UPDATED = "item.updated"
109
+ ITEM_DELETED = "item.deleted"
110
+
111
+
112
+ class Webhook(Base, TimestampMixin):
113
+ """Webhook subscription model."""
114
+
115
+ __tablename__ = "webhooks"
116
+
117
+ id: Mapped[str] = mapped_column(
118
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
119
+ )
120
+ name: Mapped[str] = mapped_column(String(255), nullable=False)
121
+ url: Mapped[str] = mapped_column(String(2048), nullable=False)
122
+ secret: Mapped[str] = mapped_column(String(255), nullable=False)
123
+ # Store events as comma-separated string for SQLite
124
+ events_json: Mapped[str] = mapped_column(Text, nullable=False)
125
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
126
+ description: Mapped[str | None] = mapped_column(Text, nullable=True)
127
+
128
+ {%- if cookiecutter.use_jwt %}
129
+ user_id: Mapped[str | None] = mapped_column(
130
+ String(36), ForeignKey("users.id"), nullable=True
131
+ )
132
+ {%- endif %}
133
+
134
+ deliveries: Mapped[list["WebhookDelivery"]] = relationship(
135
+ "WebhookDelivery", back_populates="webhook", cascade="all, delete-orphan"
136
+ )
137
+
138
+ @property
139
+ def events(self) -> list[str]:
140
+ """Parse events from JSON string."""
141
+ import json
142
+ return json.loads(self.events_json) if self.events_json else []
143
+
144
+ @events.setter
145
+ def events(self, value: list[str]) -> None:
146
+ """Store events as JSON string."""
147
+ import json
148
+ self.events_json = json.dumps(value)
149
+
150
+
151
+ class WebhookDelivery(Base):
152
+ """Webhook delivery log model."""
153
+
154
+ __tablename__ = "webhook_deliveries"
155
+
156
+ id: Mapped[str] = mapped_column(
157
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
158
+ )
159
+ webhook_id: Mapped[str] = mapped_column(
160
+ String(36), ForeignKey("webhooks.id"), nullable=False
161
+ )
162
+ event_type: Mapped[str] = mapped_column(String(100), nullable=False)
163
+ payload: Mapped[str] = mapped_column(Text, nullable=False)
164
+ response_status: Mapped[int | None] = mapped_column(Integer, nullable=True)
165
+ response_body: Mapped[str | None] = mapped_column(Text, nullable=True)
166
+ error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
167
+ attempt_count: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
168
+ success: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
169
+ created_at: Mapped[datetime] = mapped_column(
170
+ DateTime, default=datetime.utcnow, nullable=False
171
+ )
172
+ delivered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
173
+
174
+ webhook: Mapped["Webhook"] = relationship("Webhook", back_populates="deliveries")
175
+
176
+
177
+ {%- elif cookiecutter.use_mongodb %}
178
+ """Webhook document models (MongoDB)."""
179
+
180
+ from datetime import UTC, datetime
181
+ from enum import Enum
182
+ from typing import Optional
183
+
184
+ from beanie import Document
185
+ from pydantic import Field
186
+
187
+
188
+ class WebhookEventType(str, Enum):
189
+ """Webhook event types."""
190
+
191
+ # User events
192
+ USER_CREATED = "user.created"
193
+ USER_UPDATED = "user.updated"
194
+ USER_DELETED = "user.deleted"
195
+
196
+ # Custom events
197
+ ITEM_CREATED = "item.created"
198
+ ITEM_UPDATED = "item.updated"
199
+ ITEM_DELETED = "item.deleted"
200
+
201
+
202
+ class WebhookDelivery(Document):
203
+ """Webhook delivery log document."""
204
+
205
+ webhook_id: str
206
+ event_type: str
207
+ payload: str
208
+ response_status: Optional[int] = None
209
+ response_body: Optional[str] = None
210
+ error_message: Optional[str] = None
211
+ attempt_count: int = 1
212
+ success: bool = False
213
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
214
+ delivered_at: Optional[datetime] = None
215
+
216
+ class Settings:
217
+ name = "webhook_deliveries"
218
+ indexes = ["webhook_id", "event_type", "created_at"]
219
+
220
+
221
+ class Webhook(Document):
222
+ """Webhook subscription document."""
223
+
224
+ name: str
225
+ url: str
226
+ secret: str
227
+ events: list[str]
228
+ is_active: bool = True
229
+ description: Optional[str] = None
230
+ {%- if cookiecutter.use_jwt %}
231
+ user_id: Optional[str] = None
232
+ {%- endif %}
233
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
234
+ updated_at: Optional[datetime] = None
235
+
236
+ class Settings:
237
+ name = "webhooks"
238
+ indexes = ["events", "is_active"]
239
+
240
+
241
+ {%- endif %}
242
+ {%- else %}
243
+ """Webhook models - not configured."""
244
+ {%- endif %}
@@ -0,0 +1,130 @@
1
+ {%- if cookiecutter.use_postgresql %}
2
+ """Async PostgreSQL database session."""
3
+
4
+ from collections.abc import AsyncGenerator
5
+ from contextlib import asynccontextmanager
6
+
7
+ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
8
+
9
+ from app.core.config import settings
10
+
11
+ engine = create_async_engine(
12
+ settings.DATABASE_URL,
13
+ echo=settings.DEBUG,
14
+ pool_size=settings.DB_POOL_SIZE,
15
+ max_overflow=settings.DB_MAX_OVERFLOW,
16
+ pool_timeout=settings.DB_POOL_TIMEOUT,
17
+ )
18
+
19
+ async_session_maker = async_sessionmaker(
20
+ engine,
21
+ class_=AsyncSession,
22
+ expire_on_commit=False,
23
+ )
24
+
25
+
26
+ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
27
+ """Get async database session for FastAPI dependency injection.
28
+
29
+ Use this with FastAPI Depends().
30
+ """
31
+ async with async_session_maker() as session:
32
+ try:
33
+ yield session
34
+ await session.commit()
35
+ except Exception:
36
+ await session.rollback()
37
+ raise
38
+
39
+
40
+ @asynccontextmanager
41
+ async def get_db_context() -> AsyncGenerator[AsyncSession, None]:
42
+ """Get async database session as context manager.
43
+
44
+ Use this with 'async with' for manual session management (e.g., WebSockets).
45
+ """
46
+ async with async_session_maker() as session:
47
+ try:
48
+ yield session
49
+ await session.commit()
50
+ except Exception:
51
+ await session.rollback()
52
+ raise
53
+
54
+
55
+ async def close_db() -> None:
56
+ """Close database connections."""
57
+ await engine.dispose()
58
+
59
+
60
+ {%- elif cookiecutter.use_mongodb %}
61
+ """Async MongoDB database session."""
62
+
63
+ from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
64
+
65
+ from app.core.config import settings
66
+
67
+ client: AsyncIOMotorClient | None = None
68
+
69
+
70
+ async def get_db_session() -> AsyncIOMotorDatabase:
71
+ """Get MongoDB database instance."""
72
+ global client
73
+ if client is None:
74
+ client = AsyncIOMotorClient(settings.MONGO_URL)
75
+ return client[settings.MONGO_DB]
76
+
77
+
78
+ async def close_db() -> None:
79
+ """Close MongoDB connection."""
80
+ global client
81
+ if client is not None:
82
+ client.close()
83
+ client = None
84
+
85
+
86
+ {%- elif cookiecutter.use_sqlite %}
87
+ """Sync SQLite database session."""
88
+
89
+ from collections.abc import Generator
90
+
91
+ from sqlalchemy import create_engine
92
+ from sqlalchemy.orm import Session, sessionmaker
93
+
94
+ from app.core.config import settings
95
+
96
+ engine = create_engine(
97
+ settings.DATABASE_URL,
98
+ connect_args={"check_same_thread": False},
99
+ echo=settings.DEBUG,
100
+ )
101
+
102
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
103
+
104
+
105
+ def get_db_session() -> Generator[Session, None, None]:
106
+ """Get sync database session."""
107
+ db = SessionLocal()
108
+ try:
109
+ yield db
110
+ db.commit()
111
+ except Exception:
112
+ db.rollback()
113
+ raise
114
+ finally:
115
+ db.close()
116
+
117
+
118
+ def close_db() -> None:
119
+ """Close database connection."""
120
+ engine.dispose()
121
+
122
+
123
+ {%- else %}
124
+ """No database configured."""
125
+
126
+
127
+ async def get_db_session():
128
+ """No-op when database is disabled."""
129
+ yield None
130
+ {%- endif %}