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,561 @@
1
+ {%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
2
+ {%- if cookiecutter.use_postgresql %}
3
+ """Webhook service (PostgreSQL async)."""
4
+
5
+ import hashlib
6
+ import hmac
7
+ import json
8
+ import secrets
9
+ from datetime import datetime
10
+ from uuid import UUID
11
+
12
+ import httpx
13
+ import logfire
14
+ from sqlalchemy.ext.asyncio import AsyncSession
15
+
16
+ from app.core.exceptions import NotFoundError
17
+ from app.db.models.webhook import Webhook, WebhookDelivery
18
+ from app.repositories import webhook_repo
19
+ from app.schemas.webhook import WebhookCreate, WebhookUpdate
20
+
21
+
22
+ class WebhookService:
23
+ """Service for webhook management and delivery."""
24
+
25
+ def __init__(self, db: AsyncSession):
26
+ self.db = db
27
+
28
+ async def create_webhook(
29
+ self,
30
+ data: WebhookCreate,
31
+ user_id: UUID | None = None,
32
+ ) -> Webhook:
33
+ """Create a new webhook subscription."""
34
+ # Generate a secure secret for HMAC signing
35
+ secret = secrets.token_urlsafe(32)
36
+
37
+ return await webhook_repo.create(
38
+ self.db,
39
+ name=data.name,
40
+ url=str(data.url),
41
+ secret=secret,
42
+ events=data.events,
43
+ description=data.description,
44
+ user_id=user_id,
45
+ )
46
+
47
+ async def get_webhook(self, webhook_id: UUID) -> Webhook:
48
+ """Get a webhook by ID."""
49
+ webhook = await webhook_repo.get_by_id(self.db, webhook_id)
50
+ if not webhook:
51
+ raise NotFoundError(message="Webhook not found")
52
+ return webhook
53
+
54
+ async def list_webhooks(
55
+ self,
56
+ user_id: UUID | None = None,
57
+ skip: int = 0,
58
+ limit: int = 50,
59
+ ) -> tuple[list[Webhook], int]:
60
+ """List webhooks, optionally filtered by user."""
61
+ return await webhook_repo.get_list(
62
+ self.db, user_id=user_id, skip=skip, limit=limit
63
+ )
64
+
65
+ async def update_webhook(
66
+ self,
67
+ webhook_id: UUID,
68
+ data: WebhookUpdate,
69
+ ) -> Webhook:
70
+ """Update a webhook."""
71
+ webhook = await self.get_webhook(webhook_id)
72
+ return await webhook_repo.update(self.db, webhook, data)
73
+
74
+ async def delete_webhook(self, webhook_id: UUID) -> None:
75
+ """Delete a webhook."""
76
+ webhook = await self.get_webhook(webhook_id)
77
+ await webhook_repo.delete(self.db, webhook)
78
+
79
+ async def regenerate_secret(self, webhook_id: UUID) -> str:
80
+ """Regenerate the webhook secret."""
81
+ webhook = await self.get_webhook(webhook_id)
82
+ new_secret = secrets.token_urlsafe(32)
83
+ await webhook_repo.update_secret(self.db, webhook, new_secret)
84
+ return new_secret
85
+
86
+ async def test_webhook(self, webhook_id: UUID) -> dict:
87
+ """Send a test event to the webhook."""
88
+ webhook = await self.get_webhook(webhook_id)
89
+
90
+ test_payload = {
91
+ "event": "webhook.test",
92
+ "timestamp": datetime.utcnow().isoformat(),
93
+ "data": {"message": "This is a test webhook delivery"},
94
+ }
95
+
96
+ result = await self._deliver(webhook, "webhook.test", test_payload)
97
+ return result
98
+
99
+ async def dispatch_event(
100
+ self,
101
+ event_type: str,
102
+ data: dict,
103
+ ) -> None:
104
+ """Dispatch an event to all subscribed webhooks."""
105
+ webhooks = await webhook_repo.get_by_event(self.db, event_type)
106
+
107
+ payload = {
108
+ "event": event_type,
109
+ "timestamp": datetime.utcnow().isoformat(),
110
+ "data": data,
111
+ }
112
+
113
+ for webhook in webhooks:
114
+ # In production, you'd want to queue this for background processing
115
+ try:
116
+ await self._deliver(webhook, event_type, payload)
117
+ except Exception as e:
118
+ logfire.error(
119
+ "Webhook delivery failed",
120
+ webhook_id=str(webhook.id),
121
+ event_type=event_type,
122
+ error=str(e),
123
+ )
124
+
125
+ async def _deliver(
126
+ self,
127
+ webhook: Webhook,
128
+ event_type: str,
129
+ payload: dict,
130
+ ) -> dict:
131
+ """Deliver a payload to a webhook with HMAC signature."""
132
+ payload_json = json.dumps(payload, default=str)
133
+
134
+ # Create HMAC signature
135
+ signature = self._create_signature(webhook.secret, payload_json)
136
+
137
+ headers = {
138
+ "Content-Type": "application/json",
139
+ "X-Webhook-Signature": signature,
140
+ "X-Webhook-Event": event_type,
141
+ }
142
+
143
+ delivery = WebhookDelivery(
144
+ webhook_id=webhook.id,
145
+ event_type=event_type,
146
+ payload=payload_json,
147
+ )
148
+ self.db.add(delivery)
149
+ await self.db.flush()
150
+
151
+ try:
152
+ async with httpx.AsyncClient(timeout=30.0) as client:
153
+ response = await client.post(
154
+ webhook.url,
155
+ content=payload_json,
156
+ headers=headers,
157
+ )
158
+
159
+ delivery.response_status = response.status_code
160
+ delivery.response_body = response.text[:10000] # Limit size
161
+ delivery.success = 200 <= response.status_code < 300
162
+ delivery.delivered_at = datetime.utcnow()
163
+
164
+ logfire.info(
165
+ "Webhook delivered",
166
+ webhook_id=str(webhook.id),
167
+ event_type=event_type,
168
+ status_code=response.status_code,
169
+ success=delivery.success,
170
+ )
171
+
172
+ except Exception as e:
173
+ delivery.error_message = str(e)
174
+ delivery.success = False
175
+
176
+ logfire.error(
177
+ "Webhook delivery error",
178
+ webhook_id=str(webhook.id),
179
+ event_type=event_type,
180
+ error=str(e),
181
+ )
182
+
183
+ await self.db.flush()
184
+
185
+ return {
186
+ "success": delivery.success,
187
+ "status_code": delivery.response_status,
188
+ "message": delivery.error_message or "Delivered successfully",
189
+ }
190
+
191
+ def _create_signature(self, secret: str, payload: str) -> str:
192
+ """Create HMAC-SHA256 signature for the payload."""
193
+ signature = hmac.new(
194
+ secret.encode("utf-8"),
195
+ payload.encode("utf-8"),
196
+ hashlib.sha256,
197
+ ).hexdigest()
198
+ return f"sha256={signature}"
199
+
200
+ async def get_deliveries(
201
+ self,
202
+ webhook_id: UUID,
203
+ *,
204
+ skip: int = 0,
205
+ limit: int = 50,
206
+ ) -> tuple[list[WebhookDelivery], int]:
207
+ """Get delivery history for a webhook."""
208
+ # Verify webhook exists
209
+ await self.get_webhook(webhook_id)
210
+ return await webhook_repo.get_deliveries(self.db, webhook_id, skip=skip, limit=limit)
211
+
212
+ @staticmethod
213
+ def verify_signature(secret: str, payload: str, signature: str) -> bool:
214
+ """Verify a webhook signature."""
215
+ expected = hmac.new(
216
+ secret.encode("utf-8"),
217
+ payload.encode("utf-8"),
218
+ hashlib.sha256,
219
+ ).hexdigest()
220
+ expected_signature = f"sha256={expected}"
221
+ return hmac.compare_digest(expected_signature, signature)
222
+
223
+
224
+ {%- elif cookiecutter.use_sqlite %}
225
+ """Webhook service (SQLite sync)."""
226
+
227
+ import hashlib
228
+ import hmac
229
+ import json
230
+ import secrets
231
+ from datetime import datetime
232
+
233
+ import httpx
234
+ import logfire
235
+ from sqlalchemy.orm import Session as DBSession
236
+
237
+ from app.core.exceptions import NotFoundError
238
+ from app.db.models.webhook import Webhook, WebhookDelivery
239
+ from app.repositories import webhook_repo
240
+ from app.schemas.webhook import WebhookCreate, WebhookUpdate
241
+
242
+
243
+ class WebhookService:
244
+ """Service for webhook management and delivery."""
245
+
246
+ def __init__(self, db: DBSession):
247
+ self.db = db
248
+
249
+ def create_webhook(
250
+ self,
251
+ data: WebhookCreate,
252
+ user_id: str | None = None,
253
+ ) -> Webhook:
254
+ """Create a new webhook subscription."""
255
+ secret = secrets.token_urlsafe(32)
256
+
257
+ return webhook_repo.create(
258
+ self.db,
259
+ name=data.name,
260
+ url=str(data.url),
261
+ secret=secret,
262
+ events=data.events,
263
+ description=data.description,
264
+ user_id=user_id,
265
+ )
266
+
267
+ def get_webhook(self, webhook_id: str) -> Webhook:
268
+ """Get a webhook by ID."""
269
+ webhook = webhook_repo.get_by_id(self.db, webhook_id)
270
+ if not webhook:
271
+ raise NotFoundError(message="Webhook not found")
272
+ return webhook
273
+
274
+ def list_webhooks(
275
+ self,
276
+ user_id: str | None = None,
277
+ skip: int = 0,
278
+ limit: int = 50,
279
+ ) -> tuple[list[Webhook], int]:
280
+ """List webhooks, optionally filtered by user."""
281
+ return webhook_repo.get_list(
282
+ self.db, user_id=user_id, skip=skip, limit=limit
283
+ )
284
+
285
+ def update_webhook(
286
+ self,
287
+ webhook_id: str,
288
+ data: WebhookUpdate,
289
+ ) -> Webhook:
290
+ """Update a webhook."""
291
+ webhook = self.get_webhook(webhook_id)
292
+ return webhook_repo.update(self.db, webhook, data)
293
+
294
+ def delete_webhook(self, webhook_id: str) -> None:
295
+ """Delete a webhook."""
296
+ webhook = self.get_webhook(webhook_id)
297
+ webhook_repo.delete(self.db, webhook)
298
+
299
+ def dispatch_event(
300
+ self,
301
+ event_type: str,
302
+ data: dict,
303
+ ) -> None:
304
+ """Dispatch an event to all subscribed webhooks."""
305
+ webhooks = webhook_repo.get_by_event(self.db, event_type)
306
+
307
+ payload = {
308
+ "event": event_type,
309
+ "timestamp": datetime.utcnow().isoformat(),
310
+ "data": data,
311
+ }
312
+
313
+ for webhook in webhooks:
314
+ try:
315
+ self._deliver(webhook, event_type, payload)
316
+ except Exception as e:
317
+ logfire.error(
318
+ "Webhook delivery failed",
319
+ webhook_id=str(webhook.id),
320
+ event_type=event_type,
321
+ error=str(e),
322
+ )
323
+
324
+ def _deliver(
325
+ self,
326
+ webhook: Webhook,
327
+ event_type: str,
328
+ payload: dict,
329
+ ) -> dict:
330
+ """Deliver a payload to a webhook with HMAC signature."""
331
+ payload_json = json.dumps(payload, default=str)
332
+ signature = self._create_signature(webhook.secret, payload_json)
333
+
334
+ headers = {
335
+ "Content-Type": "application/json",
336
+ "X-Webhook-Signature": signature,
337
+ "X-Webhook-Event": event_type,
338
+ }
339
+
340
+ delivery = WebhookDelivery(
341
+ webhook_id=webhook.id,
342
+ event_type=event_type,
343
+ payload=payload_json,
344
+ )
345
+ self.db.add(delivery)
346
+ self.db.flush()
347
+
348
+ try:
349
+ with httpx.Client(timeout=30.0) as client:
350
+ response = client.post(
351
+ webhook.url,
352
+ content=payload_json,
353
+ headers=headers,
354
+ )
355
+
356
+ delivery.response_status = response.status_code
357
+ delivery.response_body = response.text[:10000]
358
+ delivery.success = 200 <= response.status_code < 300
359
+ delivery.delivered_at = datetime.utcnow()
360
+
361
+ except Exception as e:
362
+ delivery.error_message = str(e)
363
+ delivery.success = False
364
+
365
+ self.db.flush()
366
+
367
+ return {
368
+ "success": delivery.success,
369
+ "status_code": delivery.response_status,
370
+ "message": delivery.error_message or "Delivered successfully",
371
+ }
372
+
373
+ def _create_signature(self, secret: str, payload: str) -> str:
374
+ """Create HMAC-SHA256 signature for the payload."""
375
+ signature = hmac.new(
376
+ secret.encode("utf-8"),
377
+ payload.encode("utf-8"),
378
+ hashlib.sha256,
379
+ ).hexdigest()
380
+ return f"sha256={signature}"
381
+
382
+ def get_deliveries(
383
+ self,
384
+ webhook_id: str,
385
+ *,
386
+ skip: int = 0,
387
+ limit: int = 50,
388
+ ) -> tuple[list[WebhookDelivery], int]:
389
+ """Get delivery history for a webhook."""
390
+ # Verify webhook exists
391
+ self.get_webhook(webhook_id)
392
+ return webhook_repo.get_deliveries(self.db, webhook_id, skip=skip, limit=limit)
393
+
394
+
395
+ {%- elif cookiecutter.use_mongodb %}
396
+ """Webhook service (MongoDB)."""
397
+
398
+ import hashlib
399
+ import hmac
400
+ import json
401
+ import secrets
402
+ from datetime import UTC, datetime
403
+
404
+ import httpx
405
+ import logfire
406
+
407
+ from app.core.exceptions import NotFoundError
408
+ from app.db.models.webhook import Webhook, WebhookDelivery
409
+ from app.repositories import webhook_repo
410
+ from app.schemas.webhook import WebhookCreate, WebhookUpdate
411
+
412
+
413
+ class WebhookService:
414
+ """Service for webhook management and delivery."""
415
+
416
+ async def create_webhook(
417
+ self,
418
+ data: WebhookCreate,
419
+ user_id: str | None = None,
420
+ ) -> Webhook:
421
+ """Create a new webhook subscription."""
422
+ secret = secrets.token_urlsafe(32)
423
+
424
+ return await webhook_repo.create(
425
+ name=data.name,
426
+ url=str(data.url),
427
+ secret=secret,
428
+ events=data.events,
429
+ description=data.description,
430
+ user_id=user_id,
431
+ )
432
+
433
+ async def get_webhook(self, webhook_id: str) -> Webhook:
434
+ """Get a webhook by ID."""
435
+ webhook = await webhook_repo.get_by_id(webhook_id)
436
+ if not webhook:
437
+ raise NotFoundError(message="Webhook not found")
438
+ return webhook
439
+
440
+ async def list_webhooks(
441
+ self,
442
+ user_id: str | None = None,
443
+ skip: int = 0,
444
+ limit: int = 50,
445
+ ) -> tuple[list[Webhook], int]:
446
+ """List webhooks, optionally filtered by user."""
447
+ return await webhook_repo.get_list(user_id=user_id, skip=skip, limit=limit)
448
+
449
+ async def update_webhook(
450
+ self,
451
+ webhook_id: str,
452
+ data: WebhookUpdate,
453
+ ) -> Webhook:
454
+ """Update a webhook."""
455
+ webhook = await self.get_webhook(webhook_id)
456
+ return await webhook_repo.update(webhook, data)
457
+
458
+ async def delete_webhook(self, webhook_id: str) -> None:
459
+ """Delete a webhook."""
460
+ webhook = await self.get_webhook(webhook_id)
461
+ await webhook_repo.delete(webhook)
462
+
463
+ async def dispatch_event(
464
+ self,
465
+ event_type: str,
466
+ data: dict,
467
+ ) -> None:
468
+ """Dispatch an event to all subscribed webhooks."""
469
+ webhooks = await webhook_repo.get_by_event(event_type)
470
+
471
+ payload = {
472
+ "event": event_type,
473
+ "timestamp": datetime.now(UTC).isoformat(),
474
+ "data": data,
475
+ }
476
+
477
+ for webhook in webhooks:
478
+ try:
479
+ await self._deliver(webhook, event_type, payload)
480
+ except Exception as e:
481
+ logfire.error(
482
+ "Webhook delivery failed",
483
+ webhook_id=str(webhook.id),
484
+ event_type=event_type,
485
+ error=str(e),
486
+ )
487
+
488
+ async def _deliver(
489
+ self,
490
+ webhook: Webhook,
491
+ event_type: str,
492
+ payload: dict,
493
+ ) -> dict:
494
+ """Deliver a payload to a webhook with HMAC signature."""
495
+ payload_json = json.dumps(payload, default=str)
496
+ signature = self._create_signature(webhook.secret, payload_json)
497
+
498
+ headers = {
499
+ "Content-Type": "application/json",
500
+ "X-Webhook-Signature": signature,
501
+ "X-Webhook-Event": event_type,
502
+ }
503
+
504
+ delivery = WebhookDelivery(
505
+ webhook_id=str(webhook.id),
506
+ event_type=event_type,
507
+ payload=payload_json,
508
+ )
509
+ await delivery.insert()
510
+
511
+ try:
512
+ async with httpx.AsyncClient(timeout=30.0) as client:
513
+ response = await client.post(
514
+ webhook.url,
515
+ content=payload_json,
516
+ headers=headers,
517
+ )
518
+
519
+ delivery.response_status = response.status_code
520
+ delivery.response_body = response.text[:10000]
521
+ delivery.success = 200 <= response.status_code < 300
522
+ delivery.delivered_at = datetime.now(UTC)
523
+
524
+ except Exception as e:
525
+ delivery.error_message = str(e)
526
+ delivery.success = False
527
+
528
+ await delivery.save()
529
+
530
+ return {
531
+ "success": delivery.success,
532
+ "status_code": delivery.response_status,
533
+ "message": delivery.error_message or "Delivered successfully",
534
+ }
535
+
536
+ def _create_signature(self, secret: str, payload: str) -> str:
537
+ """Create HMAC-SHA256 signature for the payload."""
538
+ signature = hmac.new(
539
+ secret.encode("utf-8"),
540
+ payload.encode("utf-8"),
541
+ hashlib.sha256,
542
+ ).hexdigest()
543
+ return f"sha256={signature}"
544
+
545
+ async def get_deliveries(
546
+ self,
547
+ webhook_id: str,
548
+ *,
549
+ skip: int = 0,
550
+ limit: int = 50,
551
+ ) -> tuple[list[WebhookDelivery], int]:
552
+ """Get delivery history for a webhook."""
553
+ # Verify webhook exists
554
+ await self.get_webhook(webhook_id)
555
+ return await webhook_repo.get_deliveries(webhook_id, skip=skip, limit=limit)
556
+
557
+
558
+ {%- endif %}
559
+ {%- else %}
560
+ """Webhook service - not configured."""
561
+ {%- endif %}
@@ -0,0 +1,5 @@
1
+ {%- if cookiecutter.use_celery or cookiecutter.use_taskiq %}
2
+ """Background workers."""
3
+ {%- else %}
4
+ # Background workers not enabled
5
+ {%- endif %}
@@ -0,0 +1,64 @@
1
+ {%- if cookiecutter.use_celery %}
2
+ """Celery application configuration."""
3
+
4
+ from celery import Celery
5
+ from celery.schedules import crontab
6
+
7
+ from app.core.config import settings
8
+ {%- if cookiecutter.enable_logfire and cookiecutter.logfire_celery %}
9
+ from app.core.logfire_setup import instrument_celery
10
+ {%- endif %}
11
+
12
+ # Create Celery app
13
+ celery_app = Celery(
14
+ "{{ cookiecutter.project_slug }}",
15
+ broker=settings.CELERY_BROKER_URL,
16
+ backend=settings.CELERY_RESULT_BACKEND,
17
+ )
18
+
19
+ # Celery configuration
20
+ celery_app.conf.update(
21
+ # Task settings
22
+ task_serializer="json",
23
+ accept_content=["json"],
24
+ result_serializer="json",
25
+ timezone="UTC",
26
+ enable_utc=True,
27
+ # Task execution settings
28
+ task_acks_late=True,
29
+ task_reject_on_worker_lost=True,
30
+ # Result settings
31
+ result_expires=3600, # 1 hour
32
+ # Worker settings
33
+ worker_prefetch_multiplier=1,
34
+ worker_concurrency=4,
35
+ )
36
+
37
+ # Autodiscover tasks from app.worker.tasks module
38
+ celery_app.autodiscover_tasks(["app.worker.tasks"])
39
+
40
+
41
+ # === Beat Schedule ===
42
+ # Add periodic tasks here
43
+ celery_app.conf.beat_schedule = {
44
+ "example-every-minute": {
45
+ "task": "app.worker.tasks.examples.example_task",
46
+ "schedule": 60.0, # Every 60 seconds
47
+ "args": ("periodic",),
48
+ },
49
+ # Example with crontab (runs at 00:00 every day)
50
+ # "daily-cleanup": {
51
+ # "task": "app.worker.tasks.examples.cleanup_task",
52
+ # "schedule": crontab(hour=0, minute=0),
53
+ # },
54
+ }
55
+
56
+ {%- if cookiecutter.enable_logfire and cookiecutter.logfire_celery %}
57
+
58
+
59
+ # Instrument Celery with Logfire
60
+ instrument_celery()
61
+ {%- endif %}
62
+ {%- else %}
63
+ # Celery not enabled for this project
64
+ {%- endif %}
@@ -0,0 +1,38 @@
1
+ {%- if cookiecutter.use_taskiq %}
2
+ """Taskiq application configuration."""
3
+
4
+ from taskiq import TaskiqScheduler
5
+ from taskiq_redis import ListQueueBroker, RedisAsyncResultBackend
6
+
7
+ from app.core.config import settings
8
+
9
+ # Create Taskiq broker with Redis
10
+ broker = ListQueueBroker(
11
+ url=settings.TASKIQ_BROKER_URL,
12
+ ).with_result_backend(
13
+ RedisAsyncResultBackend(
14
+ redis_url=settings.TASKIQ_RESULT_BACKEND,
15
+ )
16
+ )
17
+
18
+ # Create scheduler for periodic tasks
19
+ scheduler = TaskiqScheduler(
20
+ broker=broker,
21
+ sources=["app.worker.tasks.schedules"],
22
+ )
23
+
24
+
25
+ # Startup/shutdown hooks
26
+ @broker.on_event("startup")
27
+ async def startup() -> None:
28
+ """Initialize broker on startup."""
29
+ pass
30
+
31
+
32
+ @broker.on_event("shutdown")
33
+ async def shutdown() -> None:
34
+ """Cleanup on shutdown."""
35
+ pass
36
+ {%- else %}
37
+ # Taskiq not enabled for this project
38
+ {%- endif %}
@@ -0,0 +1,25 @@
1
+ {%- if cookiecutter.use_celery or cookiecutter.use_taskiq %}
2
+ """Background tasks."""
3
+
4
+ {%- if cookiecutter.use_celery %}
5
+ from app.worker.tasks.examples import example_task, long_running_task
6
+ {%- endif %}
7
+
8
+ {%- if cookiecutter.use_taskiq %}
9
+ from app.worker.tasks.taskiq_examples import example_task as taskiq_example_task
10
+ from app.worker.tasks.taskiq_examples import long_running_task as taskiq_long_running_task
11
+ {%- endif %}
12
+
13
+ __all__ = [
14
+ {%- if cookiecutter.use_celery %}
15
+ "example_task",
16
+ "long_running_task",
17
+ {%- endif %}
18
+ {%- if cookiecutter.use_taskiq %}
19
+ "taskiq_example_task",
20
+ "taskiq_long_running_task",
21
+ {%- endif %}
22
+ ]
23
+ {%- else %}
24
+ # Background tasks not enabled
25
+ {%- endif %}