fastapi-fullstack 0.1.2__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 (221) hide show
  1. fastapi_fullstack-0.1.2.dist-info/METADATA +545 -0
  2. fastapi_fullstack-0.1.2.dist-info/RECORD +221 -0
  3. fastapi_fullstack-0.1.2.dist-info/WHEEL +4 -0
  4. fastapi_fullstack-0.1.2.dist-info/entry_points.txt +2 -0
  5. fastapi_fullstack-0.1.2.dist-info/licenses/LICENSE +21 -0
  6. fastapi_gen/__init__.py +3 -0
  7. fastapi_gen/cli.py +256 -0
  8. fastapi_gen/config.py +255 -0
  9. fastapi_gen/generator.py +181 -0
  10. fastapi_gen/prompts.py +648 -0
  11. fastapi_gen/template/cookiecutter.json +76 -0
  12. fastapi_gen/template/hooks/post_gen_project.py +111 -0
  13. fastapi_gen/template/{{cookiecutter.project_slug}}/.env.example +136 -0
  14. fastapi_gen/template/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +150 -0
  15. fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore +108 -0
  16. fastapi_gen/template/{{cookiecutter.project_slug}}/CLAUDE.md +357 -0
  17. fastapi_gen/template/{{cookiecutter.project_slug}}/Makefile +298 -0
  18. fastapi_gen/template/{{cookiecutter.project_slug}}/README.md +723 -0
  19. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.dockerignore +60 -0
  20. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml +32 -0
  21. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile +56 -0
  22. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py +76 -0
  23. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako +30 -0
  24. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep +0 -0
  25. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini +48 -0
  26. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py +3 -0
  27. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +115 -0
  28. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py +13 -0
  29. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py +202 -0
  30. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/__init__.py +13 -0
  31. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/datetime_tool.py +17 -0
  32. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py +1 -0
  33. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +528 -0
  34. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py +85 -0
  35. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py +10 -0
  36. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py +9 -0
  37. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py +87 -0
  38. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py +448 -0
  39. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +395 -0
  40. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/conversations.py +490 -0
  41. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py +227 -0
  42. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py +275 -0
  43. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py +205 -0
  44. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/sessions.py +168 -0
  45. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/users.py +333 -0
  46. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/webhooks.py +477 -0
  47. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/ws.py +46 -0
  48. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +221 -0
  49. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/__init__.py +14 -0
  50. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/clients/redis.py +88 -0
  51. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/__init__.py +117 -0
  52. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/cleanup.py +75 -0
  53. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/example.py +28 -0
  54. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/commands/seed.py +266 -0
  55. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/__init__.py +5 -0
  56. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py +23 -0
  57. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py +247 -0
  58. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +153 -0
  59. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py +122 -0
  60. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py +101 -0
  61. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py +99 -0
  62. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py +23 -0
  63. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +58 -0
  64. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py +271 -0
  65. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +102 -0
  66. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py +7 -0
  67. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py +41 -0
  68. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/__init__.py +31 -0
  69. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/conversation.py +319 -0
  70. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/item.py +96 -0
  71. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/session.py +126 -0
  72. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +218 -0
  73. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py +244 -0
  74. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py +113 -0
  75. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +326 -0
  76. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/__init__.py +9 -0
  77. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/pipelines/base.py +73 -0
  78. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py +49 -0
  79. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/base.py +154 -0
  80. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/conversation.py +760 -0
  81. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/item.py +222 -0
  82. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/session.py +318 -0
  83. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/user.py +322 -0
  84. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/webhook.py +358 -0
  85. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/__init__.py +50 -0
  86. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/base.py +57 -0
  87. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/conversation.py +195 -0
  88. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/item.py +52 -0
  89. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/session.py +42 -0
  90. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py +31 -0
  91. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py +64 -0
  92. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +89 -0
  93. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py +38 -0
  94. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/conversation.py +797 -0
  95. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/item.py +246 -0
  96. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py +333 -0
  97. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +432 -0
  98. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py +561 -0
  99. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py +5 -0
  100. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py +64 -0
  101. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/taskiq_app.py +38 -0
  102. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/__init__.py +25 -0
  103. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/examples.py +106 -0
  104. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/schedules.py +29 -0
  105. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/tasks/taskiq_examples.py +92 -0
  106. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py +1 -0
  107. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py +438 -0
  108. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +158 -0
  109. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep +0 -0
  110. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py +1 -0
  111. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py +1 -0
  112. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +242 -0
  113. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +151 -0
  114. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py +113 -0
  115. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_items.py +310 -0
  116. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py +253 -0
  117. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +151 -0
  118. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py +121 -0
  119. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py +183 -0
  120. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py +173 -0
  121. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +143 -0
  122. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_pipelines.py +118 -0
  123. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py +181 -0
  124. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +124 -0
  125. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +363 -0
  126. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker.py +85 -0
  127. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.dev.yml +242 -0
  128. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.frontend.yml +31 -0
  129. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.prod.yml +382 -0
  130. fastapi_gen/template/{{cookiecutter.project_slug}}/docker-compose.yml +241 -0
  131. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.env.example +12 -0
  132. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.gitignore +45 -0
  133. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierignore +19 -0
  134. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/.prettierrc +11 -0
  135. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/Dockerfile +44 -0
  136. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/README.md +693 -0
  137. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.setup.ts +49 -0
  138. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/auth.spec.ts +134 -0
  139. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/chat.spec.ts +207 -0
  140. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/e2e/home.spec.ts +73 -0
  141. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/instrumentation.ts +14 -0
  142. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/en.json +84 -0
  143. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/messages/pl.json +84 -0
  144. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/next.config.ts +76 -0
  145. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/package.json +66 -0
  146. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/playwright.config.ts +101 -0
  147. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/postcss.config.mjs +7 -0
  148. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/layout.tsx +11 -0
  149. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/login/page.tsx +5 -0
  150. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(auth)/register/page.tsx +5 -0
  151. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/chat/page.tsx +20 -0
  152. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/dashboard/page.tsx +99 -0
  153. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/layout.tsx +17 -0
  154. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/(dashboard)/profile/page.tsx +156 -0
  155. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/login/route.ts +58 -0
  156. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/logout/route.ts +24 -0
  157. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/me/route.ts +39 -0
  158. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/oauth-callback/route.ts +50 -0
  159. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/refresh/route.ts +54 -0
  160. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/auth/register/route.ts +26 -0
  161. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/messages/route.ts +41 -0
  162. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/[id]/route.ts +108 -0
  163. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/conversations/route.ts +73 -0
  164. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/api/health/route.ts +21 -0
  165. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/auth/callback/page.tsx +96 -0
  166. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/globals.css +108 -0
  167. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/layout.tsx +25 -0
  168. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/page.tsx +73 -0
  169. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/app/providers.tsx +29 -0
  170. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/index.ts +2 -0
  171. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/login-form.tsx +120 -0
  172. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/auth/register-form.tsx +153 -0
  173. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-container.tsx +135 -0
  174. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/chat-input.tsx +73 -0
  175. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/conversation-sidebar.tsx +261 -0
  176. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/index.ts +8 -0
  177. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-item.tsx +63 -0
  178. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/message-list.tsx +18 -0
  179. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/chat/tool-call-card.tsx +60 -0
  180. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/google-icon.tsx +32 -0
  181. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/icons/index.ts +3 -0
  182. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/language-switcher.tsx +97 -0
  183. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/header.tsx +45 -0
  184. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/index.ts +2 -0
  185. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/layout/sidebar.tsx +48 -0
  186. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/index.ts +7 -0
  187. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-provider.tsx +53 -0
  188. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/theme/theme-toggle.tsx +83 -0
  189. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/badge.tsx +35 -0
  190. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.test.tsx +75 -0
  191. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/button.tsx +54 -0
  192. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/card.tsx +82 -0
  193. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/index.ts +12 -0
  194. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/input.tsx +21 -0
  195. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/components/ui/label.tsx +21 -0
  196. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/index.ts +6 -0
  197. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-auth.ts +97 -0
  198. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-chat.ts +203 -0
  199. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-conversations.ts +175 -0
  200. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/hooks/use-websocket.ts +105 -0
  201. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/i18n.ts +32 -0
  202. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/api-client.ts +90 -0
  203. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/constants.ts +39 -0
  204. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/server-api.ts +78 -0
  205. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.test.ts +44 -0
  206. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/lib/utils.ts +44 -0
  207. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/middleware.ts +33 -0
  208. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.test.ts +72 -0
  209. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/auth-store.ts +48 -0
  210. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/chat-store.ts +65 -0
  211. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/conversation-store.ts +76 -0
  212. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/index.ts +6 -0
  213. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/stores/theme-store.ts +44 -0
  214. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/api.ts +27 -0
  215. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/auth.ts +52 -0
  216. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/chat.ts +81 -0
  217. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/conversation.ts +49 -0
  218. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/src/types/index.ts +10 -0
  219. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/tsconfig.json +28 -0
  220. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.config.ts +36 -0
  221. fastapi_gen/template/{{cookiecutter.project_slug}}/frontend/vitest.setup.ts +56 -0
@@ -0,0 +1,126 @@
1
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
2
+ {%- if cookiecutter.use_postgresql %}
3
+ """Session database model for tracking user sessions."""
4
+
5
+ import uuid
6
+ from datetime import datetime
7
+
8
+ from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text
9
+ from sqlalchemy.dialects.postgresql import UUID
10
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
11
+
12
+ from app.db.base import Base
13
+
14
+
15
+ class Session(Base):
16
+ """User session model for tracking active login sessions."""
17
+
18
+ __tablename__ = "sessions"
19
+
20
+ id: Mapped[uuid.UUID] = mapped_column(
21
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
22
+ )
23
+ user_id: Mapped[uuid.UUID] = mapped_column(
24
+ UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False
25
+ )
26
+ refresh_token_hash: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
27
+ device_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
28
+ device_type: Mapped[str | None] = mapped_column(String(50), nullable=True)
29
+ ip_address: Mapped[str | None] = mapped_column(String(45), nullable=True)
30
+ user_agent: Mapped[str | None] = mapped_column(Text, nullable=True)
31
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
32
+ created_at: Mapped[datetime] = mapped_column(
33
+ DateTime(timezone=True), default=datetime.utcnow, nullable=False
34
+ )
35
+ last_used_at: Mapped[datetime] = mapped_column(
36
+ DateTime(timezone=True), default=datetime.utcnow, nullable=False
37
+ )
38
+ expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
39
+
40
+ # Relationship
41
+ user = relationship("User", back_populates="sessions")
42
+
43
+ def __repr__(self) -> str:
44
+ return f"<Session(id={self.id}, user_id={self.user_id}, device={self.device_name})>"
45
+
46
+
47
+ {%- elif cookiecutter.use_sqlite %}
48
+ """Session database model for tracking user sessions."""
49
+
50
+ import uuid
51
+ from datetime import datetime
52
+
53
+ from sqlalchemy import Boolean, DateTime, ForeignKey, String, Text
54
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
55
+
56
+ from app.db.base import Base
57
+
58
+
59
+ class Session(Base):
60
+ """User session model for tracking active login sessions."""
61
+
62
+ __tablename__ = "sessions"
63
+
64
+ id: Mapped[str] = mapped_column(
65
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
66
+ )
67
+ user_id: Mapped[str] = mapped_column(
68
+ String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False
69
+ )
70
+ refresh_token_hash: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
71
+ device_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
72
+ device_type: Mapped[str | None] = mapped_column(String(50), nullable=True)
73
+ ip_address: Mapped[str | None] = mapped_column(String(45), nullable=True)
74
+ user_agent: Mapped[str | None] = mapped_column(Text, nullable=True)
75
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
76
+ created_at: Mapped[datetime] = mapped_column(
77
+ DateTime, default=datetime.utcnow, nullable=False
78
+ )
79
+ last_used_at: Mapped[datetime] = mapped_column(
80
+ DateTime, default=datetime.utcnow, nullable=False
81
+ )
82
+ expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
83
+
84
+ # Relationship
85
+ user = relationship("User", back_populates="sessions")
86
+
87
+ def __repr__(self) -> str:
88
+ return f"<Session(id={self.id}, user_id={self.user_id}, device={self.device_name})>"
89
+
90
+
91
+ {%- elif cookiecutter.use_mongodb %}
92
+ """Session document model for tracking user sessions."""
93
+
94
+ from datetime import UTC, datetime
95
+ from typing import Optional
96
+
97
+ from beanie import Document, Link
98
+ from pydantic import Field
99
+
100
+
101
+ class Session(Document):
102
+ """User session document for tracking active login sessions."""
103
+
104
+ user_id: str
105
+ refresh_token_hash: str
106
+ device_name: Optional[str] = None
107
+ device_type: Optional[str] = None
108
+ ip_address: Optional[str] = None
109
+ user_agent: Optional[str] = None
110
+ is_active: bool = True
111
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
112
+ last_used_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
113
+ expires_at: datetime
114
+
115
+ class Settings:
116
+ name = "sessions"
117
+ indexes = [
118
+ "user_id",
119
+ "refresh_token_hash",
120
+ ]
121
+
122
+
123
+ {%- endif %}
124
+ {%- else %}
125
+ """Session model - not configured."""
126
+ {%- endif %}
@@ -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,113 @@
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
+ @asynccontextmanager
27
+ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
28
+ """Get async database session.
29
+
30
+ Can be used both as a FastAPI dependency and as an async context manager.
31
+ """
32
+ async with async_session_maker() as session:
33
+ try:
34
+ yield session
35
+ await session.commit()
36
+ except Exception:
37
+ await session.rollback()
38
+ raise
39
+ finally:
40
+ await session.close()
41
+
42
+
43
+ async def close_db() -> None:
44
+ """Close database connections."""
45
+ await engine.dispose()
46
+
47
+
48
+ {%- elif cookiecutter.use_mongodb %}
49
+ """Async MongoDB database session."""
50
+
51
+ from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
52
+
53
+ from app.core.config import settings
54
+
55
+ client: AsyncIOMotorClient | None = None
56
+
57
+
58
+ async def get_db_session() -> AsyncIOMotorDatabase:
59
+ """Get MongoDB database instance."""
60
+ global client
61
+ if client is None:
62
+ client = AsyncIOMotorClient(settings.MONGO_URL)
63
+ return client[settings.MONGO_DB]
64
+
65
+
66
+ async def close_db() -> None:
67
+ """Close MongoDB connection."""
68
+ global client
69
+ if client is not None:
70
+ client.close()
71
+ client = None
72
+
73
+
74
+ {%- elif cookiecutter.use_sqlite %}
75
+ """Sync SQLite database session."""
76
+
77
+ from collections.abc import Generator
78
+
79
+ from sqlalchemy import create_engine
80
+ from sqlalchemy.orm import Session, sessionmaker
81
+
82
+ from app.core.config import settings
83
+
84
+ engine = create_engine(
85
+ settings.DATABASE_URL,
86
+ connect_args={"check_same_thread": False},
87
+ echo=settings.DEBUG,
88
+ )
89
+
90
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
91
+
92
+
93
+ def get_db_session() -> Generator[Session, None, None]:
94
+ """Get sync database session."""
95
+ db = SessionLocal()
96
+ try:
97
+ yield db
98
+ db.commit()
99
+ except Exception:
100
+ db.rollback()
101
+ raise
102
+ finally:
103
+ db.close()
104
+
105
+
106
+ {%- else %}
107
+ """No database configured."""
108
+
109
+
110
+ async def get_db_session():
111
+ """No-op when database is disabled."""
112
+ yield None
113
+ {%- endif %}