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,50 @@
1
+ """Pydantic schemas."""
2
+ {%- set schemas = [] %}
3
+ {%- if cookiecutter.use_jwt or cookiecutter.include_example_crud or cookiecutter.enable_conversation_persistence or cookiecutter.enable_webhooks %}
4
+ # ruff: noqa: I001, RUF022 - Imports structured for Jinja2 template conditionals
5
+ {%- endif %}
6
+ {%- if cookiecutter.use_jwt %}
7
+ {%- set _ = schemas.extend(["UserCreate", "UserRead", "UserUpdate", "Token", "TokenPayload"]) %}
8
+
9
+ from app.schemas.token import Token, TokenPayload
10
+ from app.schemas.user import UserCreate, UserRead, UserUpdate
11
+ {%- endif %}
12
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
13
+ {%- set _ = schemas.extend(["SessionRead", "SessionListResponse", "LogoutAllResponse"]) %}
14
+
15
+ from app.schemas.session import SessionRead, SessionListResponse, LogoutAllResponse
16
+ {%- endif %}
17
+ {%- if cookiecutter.include_example_crud and cookiecutter.use_database %}
18
+ {%- set _ = schemas.extend(["ItemCreate", "ItemRead", "ItemUpdate"]) %}
19
+
20
+ from app.schemas.item import ItemCreate, ItemRead, ItemUpdate
21
+ {%- endif %}
22
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
23
+ {%- set _ = schemas.extend(["ConversationCreate", "ConversationRead", "ConversationUpdate", "MessageCreate", "MessageRead", "ToolCallRead"]) %}
24
+
25
+ from app.schemas.conversation import (
26
+ ConversationCreate,
27
+ ConversationRead,
28
+ ConversationUpdate,
29
+ MessageCreate,
30
+ MessageRead,
31
+ ToolCallRead,
32
+ )
33
+ {%- endif %}
34
+ {%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
35
+ {%- set _ = schemas.extend(["WebhookCreate", "WebhookRead", "WebhookUpdate", "WebhookDeliveryRead", "WebhookListResponse", "WebhookDeliveryListResponse", "WebhookTestResponse"]) %}
36
+
37
+ from app.schemas.webhook import (
38
+ WebhookCreate,
39
+ WebhookRead,
40
+ WebhookUpdate,
41
+ WebhookDeliveryRead,
42
+ WebhookListResponse,
43
+ WebhookDeliveryListResponse,
44
+ WebhookTestResponse,
45
+ )
46
+ {%- endif %}
47
+ {%- if schemas %}
48
+
49
+ __all__ = {{ schemas }}
50
+ {%- endif %}
@@ -0,0 +1,57 @@
1
+ """Base Pydantic schemas."""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+ from zoneinfo import ZoneInfo
6
+
7
+ from pydantic import BaseModel, ConfigDict
8
+
9
+
10
+ def serialize_datetime(dt: datetime) -> str:
11
+ """Serialize datetime to ISO format with timezone.
12
+
13
+ Ensures all datetimes have explicit timezone (defaults to UTC).
14
+ """
15
+ if dt.tzinfo is None:
16
+ dt = dt.replace(tzinfo=ZoneInfo("UTC"))
17
+ return dt.isoformat()
18
+
19
+
20
+ class BaseSchema(BaseModel):
21
+ """Base schema with common configuration."""
22
+
23
+ model_config = ConfigDict(
24
+ from_attributes=True,
25
+ populate_by_name=True,
26
+ str_strip_whitespace=True,
27
+ json_encoders={datetime: serialize_datetime},
28
+ )
29
+
30
+ def serializable_dict(self, **kwargs: Any) -> dict[str, Any]:
31
+ """Return a dict with only JSON-serializable fields."""
32
+ from fastapi.encoders import jsonable_encoder
33
+
34
+ return jsonable_encoder(self.model_dump(**kwargs))
35
+
36
+
37
+ class TimestampSchema(BaseModel):
38
+ """Schema with timestamp fields."""
39
+
40
+ created_at: datetime
41
+ updated_at: datetime | None = None
42
+
43
+
44
+ class BaseResponse(BaseModel):
45
+ """Standard API response."""
46
+
47
+ success: bool = True
48
+ message: str | None = None
49
+
50
+
51
+ class ErrorResponse(BaseModel):
52
+ """Standard error response."""
53
+
54
+ success: bool = False
55
+ error: str
56
+ detail: str | None = None
57
+ code: str | None = None
@@ -0,0 +1,192 @@
1
+ {%- if cookiecutter.enable_conversation_persistence %}
2
+ """Conversation schemas for AI chat persistence.
3
+
4
+ This module contains Pydantic schemas for Conversation, Message, and ToolCall entities.
5
+ """
6
+
7
+ from datetime import datetime
8
+ from typing import Literal
9
+
10
+ {%- if cookiecutter.use_postgresql %}
11
+ from uuid import UUID
12
+ {%- endif %}
13
+
14
+ from pydantic import Field
15
+
16
+ from app.schemas.base import BaseSchema, TimestampSchema
17
+
18
+
19
+ # =============================================================================
20
+ # Tool Call Schemas
21
+ # =============================================================================
22
+
23
+
24
+ class ToolCallBase(BaseSchema):
25
+ """Base tool call schema."""
26
+
27
+ tool_call_id: str = Field(..., description="External tool call ID from AI framework")
28
+ tool_name: str = Field(..., max_length=100, description="Name of the tool called")
29
+ args: dict = Field(default_factory=dict, description="Tool arguments")
30
+
31
+
32
+ class ToolCallCreate(ToolCallBase):
33
+ """Schema for creating a tool call record."""
34
+
35
+ started_at: datetime | None = Field(default=None, description="When the tool call started")
36
+
37
+
38
+ class ToolCallComplete(BaseSchema):
39
+ """Schema for completing a tool call."""
40
+
41
+ result: str = Field(..., description="Tool execution result")
42
+ completed_at: datetime | None = Field(default=None, description="When the tool call completed")
43
+ success: bool = Field(default=True, description="Whether the tool call succeeded")
44
+
45
+
46
+ class ToolCallRead(ToolCallBase):
47
+ """Schema for reading a tool call (API response)."""
48
+
49
+ {%- if cookiecutter.use_postgresql %}
50
+ id: UUID
51
+ message_id: UUID
52
+ {%- else %}
53
+ id: str
54
+ message_id: str
55
+ {%- endif %}
56
+ result: str | None = None
57
+ status: Literal["pending", "running", "completed", "failed"] = "pending"
58
+ started_at: datetime
59
+ completed_at: datetime | None = None
60
+ duration_ms: int | None = None
61
+
62
+
63
+ # =============================================================================
64
+ # Message Schemas
65
+ # =============================================================================
66
+
67
+
68
+ class MessageBase(BaseSchema):
69
+ """Base message schema."""
70
+
71
+ role: Literal["user", "assistant", "system"] = Field(..., description="Message role")
72
+ content: str = Field(..., description="Message content")
73
+
74
+
75
+ class MessageCreate(MessageBase):
76
+ """Schema for creating a message."""
77
+
78
+ model_name: str | None = Field(default=None, max_length=100, description="AI model used")
79
+ tokens_used: int | None = Field(default=None, ge=0, description="Token count")
80
+
81
+
82
+ class MessageRead(MessageBase, TimestampSchema):
83
+ """Schema for reading a message (API response)."""
84
+
85
+ {%- if cookiecutter.use_postgresql %}
86
+ id: UUID
87
+ conversation_id: UUID
88
+ {%- else %}
89
+ id: str
90
+ conversation_id: str
91
+ {%- endif %}
92
+ model_name: str | None = None
93
+ tokens_used: int | None = None
94
+ tool_calls: list[ToolCallRead] = Field(default_factory=list)
95
+
96
+
97
+ class MessageReadSimple(MessageBase, TimestampSchema):
98
+ """Simplified message schema without tool calls."""
99
+
100
+ {%- if cookiecutter.use_postgresql %}
101
+ id: UUID
102
+ conversation_id: UUID
103
+ {%- else %}
104
+ id: str
105
+ conversation_id: str
106
+ {%- endif %}
107
+ model_name: str | None = None
108
+ tokens_used: int | None = None
109
+
110
+
111
+ # =============================================================================
112
+ # Conversation Schemas
113
+ # =============================================================================
114
+
115
+
116
+ class ConversationBase(BaseSchema):
117
+ """Base conversation schema."""
118
+
119
+ title: str | None = Field(default=None, max_length=255, description="Conversation title")
120
+
121
+
122
+ class ConversationCreate(ConversationBase):
123
+ """Schema for creating a conversation."""
124
+
125
+ {%- if cookiecutter.use_jwt %}
126
+ {%- if cookiecutter.use_postgresql %}
127
+ user_id: UUID | None = Field(default=None, description="Owner user ID")
128
+ {%- else %}
129
+ user_id: str | None = Field(default=None, description="Owner user ID")
130
+ {%- endif %}
131
+ {%- endif %}
132
+ pass
133
+
134
+
135
+ class ConversationUpdate(BaseSchema):
136
+ """Schema for updating a conversation."""
137
+
138
+ title: str | None = Field(default=None, max_length=255)
139
+ is_archived: bool | None = None
140
+
141
+
142
+ class ConversationRead(ConversationBase, TimestampSchema):
143
+ """Schema for reading a conversation (API response)."""
144
+
145
+ {%- if cookiecutter.use_postgresql %}
146
+ id: UUID
147
+ {%- if cookiecutter.use_jwt %}
148
+ user_id: UUID | None = None
149
+ {%- endif %}
150
+ {%- else %}
151
+ id: str
152
+ {%- if cookiecutter.use_jwt %}
153
+ user_id: str | None = None
154
+ {%- endif %}
155
+ {%- endif %}
156
+ is_archived: bool = False
157
+
158
+
159
+ class ConversationReadWithMessages(ConversationRead):
160
+ """Conversation with all messages."""
161
+
162
+ messages: list[MessageRead] = Field(default_factory=list)
163
+
164
+
165
+ class ConversationList(BaseSchema):
166
+ """Schema for listing conversations."""
167
+
168
+ items: list[ConversationRead]
169
+ total: int
170
+
171
+
172
+ # =============================================================================
173
+ # Aggregated Schemas for API Responses
174
+ # =============================================================================
175
+
176
+
177
+ class MessageList(BaseSchema):
178
+ """Schema for listing messages."""
179
+
180
+ items: list[MessageReadSimple]
181
+ total: int
182
+
183
+
184
+ class ConversationWithLatestMessage(ConversationRead):
185
+ """Conversation with its latest message for list views."""
186
+
187
+ latest_message: MessageReadSimple | None = None
188
+ message_count: int = 0
189
+
190
+ {%- else %}
191
+ """Conversation schemas - not configured."""
192
+ {%- endif %}
@@ -0,0 +1,52 @@
1
+ {%- if cookiecutter.include_example_crud %}
2
+ """Item schemas - example CRUD entity.
3
+
4
+ This module demonstrates standard Pydantic schemas for a CRUD entity.
5
+ You can use it as a template for creating your own schemas.
6
+ """
7
+
8
+ {%- if cookiecutter.use_postgresql %}
9
+ from uuid import UUID
10
+ {%- endif %}
11
+
12
+ from pydantic import Field
13
+
14
+ from app.schemas.base import BaseSchema, TimestampSchema
15
+
16
+
17
+ class ItemBase(BaseSchema):
18
+ """Base item schema with common fields."""
19
+
20
+ title: str = Field(max_length=255, description="Item title")
21
+ description: str | None = Field(default=None, description="Item description")
22
+
23
+
24
+ class ItemCreate(ItemBase):
25
+ """Schema for creating an item."""
26
+
27
+ pass
28
+
29
+
30
+ class ItemUpdate(BaseSchema):
31
+ """Schema for updating an item.
32
+
33
+ All fields are optional to allow partial updates.
34
+ """
35
+
36
+ title: str | None = Field(default=None, max_length=255)
37
+ description: str | None = Field(default=None)
38
+ is_active: bool | None = None
39
+
40
+
41
+ class ItemRead(ItemBase, TimestampSchema):
42
+ """Schema for reading an item (API response)."""
43
+
44
+ {%- if cookiecutter.use_postgresql %}
45
+ id: UUID
46
+ {%- else %}
47
+ id: str
48
+ {%- endif %}
49
+ is_active: bool = True
50
+ {%- else %}
51
+ """Item schemas - not configured."""
52
+ {%- endif %}
@@ -0,0 +1,42 @@
1
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
2
+ """Session schemas."""
3
+
4
+ from datetime import datetime
5
+ {%- if cookiecutter.use_postgresql %}
6
+ from uuid import UUID
7
+ {%- endif %}
8
+
9
+ from pydantic import BaseModel
10
+
11
+
12
+ class SessionRead(BaseModel):
13
+ """Session response schema."""
14
+
15
+ {%- if cookiecutter.use_postgresql %}
16
+ id: UUID
17
+ {%- else %}
18
+ id: str
19
+ {%- endif %}
20
+ device_name: str | None = None
21
+ device_type: str | None = None
22
+ ip_address: str | None = None
23
+ is_current: bool = False
24
+ created_at: datetime
25
+ last_used_at: datetime
26
+
27
+
28
+ class SessionListResponse(BaseModel):
29
+ """Response for list of sessions."""
30
+
31
+ sessions: list[SessionRead]
32
+ total: int
33
+
34
+
35
+ class LogoutAllResponse(BaseModel):
36
+ """Response for logout all sessions."""
37
+
38
+ message: str
39
+ sessions_logged_out: int
40
+ {%- else %}
41
+ """Session schemas - not configured."""
42
+ {%- endif %}
@@ -0,0 +1,31 @@
1
+ {%- if cookiecutter.use_jwt %}
2
+ """Token schemas."""
3
+
4
+ from typing import Literal
5
+
6
+ from pydantic import BaseModel
7
+
8
+
9
+ class Token(BaseModel):
10
+ """OAuth2 token response with refresh token."""
11
+
12
+ access_token: str
13
+ refresh_token: str
14
+ token_type: str = "bearer"
15
+
16
+
17
+ class TokenPayload(BaseModel):
18
+ """JWT token payload."""
19
+
20
+ sub: str | None = None
21
+ exp: int | None = None
22
+ type: Literal["access", "refresh"] | None = None
23
+
24
+
25
+ class RefreshTokenRequest(BaseModel):
26
+ """Request body for token refresh."""
27
+
28
+ refresh_token: str
29
+ {%- else %}
30
+ """Token schemas - not configured."""
31
+ {%- endif %}
@@ -0,0 +1,64 @@
1
+ {%- if cookiecutter.use_jwt %}
2
+ """User schemas."""
3
+
4
+ from enum import Enum
5
+ from uuid import UUID
6
+
7
+ from pydantic import EmailStr, Field
8
+
9
+ from app.schemas.base import BaseSchema, TimestampSchema
10
+
11
+
12
+ class UserRole(str, Enum):
13
+ """User role enumeration for API schemas."""
14
+
15
+ ADMIN = "admin"
16
+ USER = "user"
17
+
18
+
19
+ class UserBase(BaseSchema):
20
+ """Base user schema."""
21
+
22
+ email: EmailStr = Field(max_length=255)
23
+ full_name: str | None = Field(default=None, max_length=255)
24
+ is_active: bool = True
25
+
26
+
27
+ class UserCreate(BaseSchema):
28
+ """Schema for creating a user."""
29
+
30
+ email: EmailStr = Field(max_length=255)
31
+ password: str = Field(min_length=8, max_length=128)
32
+ full_name: str | None = Field(default=None, max_length=255)
33
+ role: UserRole = UserRole.USER
34
+
35
+
36
+ class UserUpdate(BaseSchema):
37
+ """Schema for updating a user."""
38
+
39
+ email: EmailStr | None = Field(default=None, max_length=255)
40
+ password: str | None = Field(default=None, min_length=8, max_length=128)
41
+ full_name: str | None = Field(default=None, max_length=255)
42
+ is_active: bool | None = None
43
+ role: UserRole | None = None
44
+
45
+
46
+ class UserRead(UserBase, TimestampSchema):
47
+ """Schema for reading a user."""
48
+
49
+ {%- if cookiecutter.use_postgresql %}
50
+ id: UUID
51
+ {%- elif cookiecutter.use_sqlite or cookiecutter.use_mongodb %}
52
+ id: str
53
+ {%- endif %}
54
+ is_superuser: bool = False
55
+ role: UserRole = UserRole.USER
56
+
57
+
58
+ class UserInDB(UserRead):
59
+ """User schema with hashed password (internal use)."""
60
+
61
+ hashed_password: str
62
+ {%- else %}
63
+ """User schemas - not configured."""
64
+ {%- endif %}
@@ -0,0 +1,89 @@
1
+ {%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
2
+ """Webhook schemas."""
3
+
4
+ from datetime import datetime
5
+ {%- if cookiecutter.use_postgresql %}
6
+ from uuid import UUID
7
+ {%- endif %}
8
+
9
+ from pydantic import BaseModel, Field, HttpUrl
10
+
11
+
12
+ class WebhookCreate(BaseModel):
13
+ """Schema for creating a webhook."""
14
+
15
+ name: str = Field(..., min_length=1, max_length=255)
16
+ url: HttpUrl
17
+ events: list[str] = Field(..., min_length=1)
18
+ description: str | None = None
19
+
20
+
21
+ class WebhookUpdate(BaseModel):
22
+ """Schema for updating a webhook."""
23
+
24
+ name: str | None = Field(None, min_length=1, max_length=255)
25
+ url: HttpUrl | None = None
26
+ events: list[str] | None = Field(None, min_length=1)
27
+ is_active: bool | None = None
28
+ description: str | None = None
29
+
30
+
31
+ class WebhookRead(BaseModel):
32
+ """Schema for reading a webhook."""
33
+
34
+ {%- if cookiecutter.use_postgresql %}
35
+ id: UUID
36
+ {%- else %}
37
+ id: str
38
+ {%- endif %}
39
+ name: str
40
+ url: str
41
+ events: list[str]
42
+ is_active: bool
43
+ description: str | None
44
+ created_at: datetime
45
+ updated_at: datetime
46
+
47
+
48
+ class WebhookDeliveryRead(BaseModel):
49
+ """Schema for reading a webhook delivery."""
50
+
51
+ {%- if cookiecutter.use_postgresql %}
52
+ id: UUID
53
+ webhook_id: UUID
54
+ {%- else %}
55
+ id: str
56
+ webhook_id: str
57
+ {%- endif %}
58
+ event_type: str
59
+ response_status: int | None
60
+ error_message: str | None
61
+ attempt_count: int
62
+ success: bool
63
+ created_at: datetime
64
+ delivered_at: datetime | None
65
+
66
+
67
+ class WebhookListResponse(BaseModel):
68
+ """Response for list of webhooks."""
69
+
70
+ items: list[WebhookRead]
71
+ total: int
72
+
73
+
74
+ class WebhookDeliveryListResponse(BaseModel):
75
+ """Response for list of webhook deliveries."""
76
+
77
+ items: list[WebhookDeliveryRead]
78
+ total: int
79
+
80
+
81
+ class WebhookTestResponse(BaseModel):
82
+ """Response for webhook test."""
83
+
84
+ success: bool
85
+ status_code: int | None
86
+ message: str
87
+ {%- else %}
88
+ """Webhook schemas - not configured."""
89
+ {%- endif %}
@@ -0,0 +1,38 @@
1
+ """Services layer - business logic.
2
+
3
+ Services orchestrate business operations, using repositories for data access
4
+ and raising domain exceptions for error handling.
5
+ """
6
+ {%- set services = [] %}
7
+ {%- if cookiecutter.use_jwt or cookiecutter.include_example_crud or cookiecutter.enable_conversation_persistence or cookiecutter.enable_webhooks %}
8
+ # ruff: noqa: I001, RUF022 - Imports structured for Jinja2 template conditionals
9
+ {%- endif %}
10
+ {%- if cookiecutter.use_jwt %}
11
+ {%- set _ = services.append("UserService") %}
12
+
13
+ from app.services.user import UserService
14
+ {%- endif %}
15
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
16
+ {%- set _ = services.append("SessionService") %}
17
+
18
+ from app.services.session import SessionService
19
+ {%- endif %}
20
+ {%- if cookiecutter.include_example_crud and cookiecutter.use_database %}
21
+ {%- set _ = services.append("ItemService") %}
22
+
23
+ from app.services.item import ItemService
24
+ {%- endif %}
25
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_database %}
26
+ {%- set _ = services.append("ConversationService") %}
27
+
28
+ from app.services.conversation import ConversationService
29
+ {%- endif %}
30
+ {%- if cookiecutter.enable_webhooks and cookiecutter.use_database %}
31
+ {%- set _ = services.append("WebhookService") %}
32
+
33
+ from app.services.webhook import WebhookService
34
+ {%- endif %}
35
+ {%- if services %}
36
+
37
+ __all__ = {{ services }}
38
+ {%- endif %}