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,102 @@
1
+ {%- if cookiecutter.use_jwt %}
2
+ """Security utilities for JWT authentication."""
3
+
4
+ from datetime import UTC, datetime, timedelta
5
+ from typing import Any
6
+
7
+ import bcrypt
8
+ import jwt
9
+
10
+ from app.core.config import settings
11
+
12
+
13
+ def create_access_token(
14
+ subject: str | Any,
15
+ expires_delta: timedelta | None = None,
16
+ ) -> str:
17
+ """Create a JWT access token."""
18
+ if expires_delta:
19
+ expire = datetime.now(UTC) + expires_delta
20
+ else:
21
+ expire = datetime.now(UTC) + timedelta(
22
+ minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
23
+ )
24
+
25
+ to_encode = {"exp": expire, "sub": str(subject), "type": "access"}
26
+ return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
27
+
28
+
29
+ def create_refresh_token(
30
+ subject: str | Any,
31
+ expires_delta: timedelta | None = None,
32
+ ) -> str:
33
+ """Create a JWT refresh token."""
34
+ if expires_delta:
35
+ expire = datetime.now(UTC) + expires_delta
36
+ else:
37
+ expire = datetime.now(UTC) + timedelta(
38
+ minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES
39
+ )
40
+
41
+ to_encode = {"exp": expire, "sub": str(subject), "type": "refresh"}
42
+ return jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
43
+
44
+
45
+ def verify_token(token: str) -> dict[str, Any] | None:
46
+ """Verify a JWT token and return payload."""
47
+ try:
48
+ payload = jwt.decode(
49
+ token,
50
+ settings.SECRET_KEY,
51
+ algorithms=[settings.ALGORITHM],
52
+ )
53
+ return payload
54
+ except jwt.PyJWTError:
55
+ return None
56
+
57
+
58
+ def verify_password(plain_password: str, hashed_password: str) -> bool:
59
+ """Verify a password against a hash."""
60
+ return bcrypt.checkpw(
61
+ plain_password.encode("utf-8"),
62
+ hashed_password.encode("utf-8"),
63
+ )
64
+
65
+
66
+ def get_password_hash(password: str) -> str:
67
+ """Hash a password."""
68
+ return bcrypt.hashpw(
69
+ password.encode("utf-8"),
70
+ bcrypt.gensalt(),
71
+ ).decode("utf-8")
72
+
73
+
74
+ {%- elif cookiecutter.use_api_key %}
75
+ """Security utilities for API Key authentication."""
76
+
77
+ from fastapi import HTTPException, Security, status
78
+ from fastapi.security import APIKeyHeader
79
+
80
+ from app.core.config import settings
81
+
82
+ api_key_header = APIKeyHeader(name=settings.API_KEY_HEADER, auto_error=False)
83
+
84
+
85
+ async def verify_api_key(api_key: str = Security(api_key_header)) -> str:
86
+ """Verify API key from header."""
87
+ if api_key is None:
88
+ raise HTTPException(
89
+ status_code=status.HTTP_401_UNAUTHORIZED,
90
+ detail="API Key header missing",
91
+ )
92
+ if api_key != settings.API_KEY:
93
+ raise HTTPException(
94
+ status_code=status.HTTP_403_FORBIDDEN,
95
+ detail="Invalid API Key",
96
+ )
97
+ return api_key
98
+
99
+
100
+ {%- else %}
101
+ """Security - not configured."""
102
+ {%- endif %}
@@ -0,0 +1,7 @@
1
+ """Database module."""
2
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
3
+
4
+ from app.db.base import Base
5
+
6
+ __all__ = ["Base"]
7
+ {%- endif %}
@@ -0,0 +1,41 @@
1
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
2
+ """SQLAlchemy base model."""
3
+
4
+ from datetime import datetime
5
+
6
+ from sqlalchemy import DateTime, MetaData, func
7
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
8
+
9
+ # Naming convention for database constraints and indexes
10
+ # This ensures consistent naming across all migrations
11
+ NAMING_CONVENTION = {
12
+ "ix": "%(column_0_label)s_idx",
13
+ "uq": "%(table_name)s_%(column_0_name)s_key",
14
+ "ck": "%(table_name)s_%(constraint_name)s_check",
15
+ "fk": "%(table_name)s_%(column_0_name)s_fkey",
16
+ "pk": "%(table_name)s_pkey",
17
+ }
18
+
19
+
20
+ class Base(DeclarativeBase):
21
+ """Base class for all SQLAlchemy models."""
22
+
23
+ metadata = MetaData(naming_convention=NAMING_CONVENTION)
24
+
25
+
26
+ class TimestampMixin:
27
+ """Mixin for created_at and updated_at timestamps."""
28
+
29
+ created_at: Mapped[datetime] = mapped_column(
30
+ DateTime(timezone=True),
31
+ server_default=func.now(),
32
+ nullable=False,
33
+ )
34
+ updated_at: Mapped[datetime | None] = mapped_column(
35
+ DateTime(timezone=True),
36
+ onupdate=func.now(),
37
+ nullable=True,
38
+ )
39
+ {%- else %}
40
+ """Database base - not using SQLAlchemy."""
41
+ {%- endif %}
@@ -0,0 +1,31 @@
1
+ """Database models."""
2
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
3
+ # ruff: noqa: I001, RUF022 - Imports structured for Jinja2 template conditionals
4
+ {%- endif %}
5
+ {%- if cookiecutter.use_postgresql or cookiecutter.use_sqlite %}
6
+ {%- set models = [] %}
7
+ {%- if cookiecutter.use_jwt %}
8
+ {%- set _ = models.append("User") %}
9
+ from app.db.models.user import User
10
+ {%- endif %}
11
+ {%- if cookiecutter.enable_session_management and cookiecutter.use_jwt %}
12
+ {%- set _ = models.append("Session") %}
13
+ from app.db.models.session import Session
14
+ {%- endif %}
15
+ {%- if cookiecutter.include_example_crud %}
16
+ {%- set _ = models.append("Item") %}
17
+ from app.db.models.item import Item
18
+ {%- endif %}
19
+ {%- if cookiecutter.enable_conversation_persistence %}
20
+ {%- set _ = models.extend(["Conversation", "Message", "ToolCall"]) %}
21
+ from app.db.models.conversation import Conversation, Message, ToolCall
22
+ {%- endif %}
23
+ {%- if cookiecutter.enable_webhooks %}
24
+ {%- set _ = models.extend(["Webhook", "WebhookDelivery"]) %}
25
+ from app.db.models.webhook import Webhook, WebhookDelivery
26
+ {%- endif %}
27
+ {%- if models %}
28
+
29
+ __all__ = {{ models }}
30
+ {%- endif %}
31
+ {%- endif %}
@@ -0,0 +1,319 @@
1
+ {%- if cookiecutter.enable_conversation_persistence and cookiecutter.use_postgresql %}
2
+ """Conversation and message models for AI chat persistence."""
3
+
4
+ import uuid
5
+ from datetime import datetime
6
+ from typing import Literal
7
+
8
+ from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
9
+ from sqlalchemy.dialects.postgresql import JSONB, UUID
10
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
11
+
12
+ from app.db.base import Base, TimestampMixin
13
+
14
+
15
+ class Conversation(Base, TimestampMixin):
16
+ """Conversation model - groups messages in a chat session.
17
+
18
+ Attributes:
19
+ id: Unique conversation identifier
20
+ user_id: Optional user who owns this conversation (if auth enabled)
21
+ title: Auto-generated or user-defined title
22
+ is_archived: Whether the conversation is archived
23
+ messages: List of messages in this conversation
24
+ """
25
+
26
+ __tablename__ = "conversations"
27
+
28
+ id: Mapped[uuid.UUID] = mapped_column(
29
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
30
+ )
31
+ {%- if cookiecutter.use_jwt %}
32
+ user_id: Mapped[uuid.UUID | None] = mapped_column(
33
+ UUID(as_uuid=True),
34
+ ForeignKey("users.id", ondelete="CASCADE"),
35
+ nullable=True,
36
+ index=True,
37
+ )
38
+ {%- endif %}
39
+ title: Mapped[str | None] = mapped_column(String(255), nullable=True)
40
+ is_archived: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
41
+
42
+ # Relationships
43
+ messages: Mapped[list["Message"]] = relationship(
44
+ "Message",
45
+ back_populates="conversation",
46
+ cascade="all, delete-orphan",
47
+ order_by="Message.created_at",
48
+ )
49
+
50
+ def __repr__(self) -> str:
51
+ return f"<Conversation(id={self.id}, title={self.title})>"
52
+
53
+
54
+ class Message(Base, TimestampMixin):
55
+ """Message model - individual message in a conversation.
56
+
57
+ Attributes:
58
+ id: Unique message identifier
59
+ conversation_id: The conversation this message belongs to
60
+ role: Message role (user, assistant, system)
61
+ content: Message text content
62
+ model_name: AI model used (for assistant messages)
63
+ tokens_used: Token count for this message
64
+ tool_calls: List of tool calls made in this message
65
+ """
66
+
67
+ __tablename__ = "messages"
68
+
69
+ id: Mapped[uuid.UUID] = mapped_column(
70
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
71
+ )
72
+ conversation_id: Mapped[uuid.UUID] = mapped_column(
73
+ UUID(as_uuid=True),
74
+ ForeignKey("conversations.id", ondelete="CASCADE"),
75
+ nullable=False,
76
+ index=True,
77
+ )
78
+ role: Mapped[str] = mapped_column(
79
+ String(20), nullable=False
80
+ ) # user, assistant, system
81
+ content: Mapped[str] = mapped_column(Text, nullable=False)
82
+ model_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
83
+ tokens_used: Mapped[int | None] = mapped_column(Integer, nullable=True)
84
+
85
+ # Relationships
86
+ conversation: Mapped["Conversation"] = relationship(
87
+ "Conversation", back_populates="messages"
88
+ )
89
+ tool_calls: Mapped[list["ToolCall"]] = relationship(
90
+ "ToolCall",
91
+ back_populates="message",
92
+ cascade="all, delete-orphan",
93
+ order_by="ToolCall.started_at",
94
+ )
95
+
96
+ def __repr__(self) -> str:
97
+ return f"<Message(id={self.id}, role={self.role})>"
98
+
99
+
100
+ class ToolCall(Base):
101
+ """ToolCall model - record of a tool invocation.
102
+
103
+ Attributes:
104
+ id: Unique tool call identifier
105
+ message_id: The assistant message that triggered this call
106
+ tool_call_id: External ID from PydanticAI
107
+ tool_name: Name of the tool that was called
108
+ args: JSON arguments passed to the tool
109
+ result: Result returned by the tool
110
+ status: Current status (pending, running, completed, failed)
111
+ started_at: When the tool call started
112
+ completed_at: When the tool call completed
113
+ duration_ms: Execution time in milliseconds
114
+ """
115
+
116
+ __tablename__ = "tool_calls"
117
+
118
+ id: Mapped[uuid.UUID] = mapped_column(
119
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
120
+ )
121
+ message_id: Mapped[uuid.UUID] = mapped_column(
122
+ UUID(as_uuid=True),
123
+ ForeignKey("messages.id", ondelete="CASCADE"),
124
+ nullable=False,
125
+ index=True,
126
+ )
127
+ tool_call_id: Mapped[str] = mapped_column(String(100), nullable=False)
128
+ tool_name: Mapped[str] = mapped_column(String(100), nullable=False)
129
+ args: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict)
130
+ result: Mapped[str | None] = mapped_column(Text, nullable=True)
131
+ status: Mapped[str] = mapped_column(
132
+ String(20), nullable=False, default="pending"
133
+ ) # pending, running, completed, failed
134
+ started_at: Mapped[datetime] = mapped_column(
135
+ DateTime(timezone=True), nullable=False
136
+ )
137
+ completed_at: Mapped[datetime | None] = mapped_column(
138
+ DateTime(timezone=True), nullable=True
139
+ )
140
+ duration_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
141
+
142
+ # Relationships
143
+ message: Mapped["Message"] = relationship("Message", back_populates="tool_calls")
144
+
145
+ def __repr__(self) -> str:
146
+ return f"<ToolCall(id={self.id}, tool_name={self.tool_name}, status={self.status})>"
147
+
148
+
149
+ {%- elif cookiecutter.enable_conversation_persistence and cookiecutter.use_sqlite %}
150
+ """Conversation and message models for AI chat persistence."""
151
+
152
+ import uuid
153
+ from datetime import datetime
154
+
155
+ from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
156
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
157
+
158
+ from app.db.base import Base, TimestampMixin
159
+
160
+
161
+ class Conversation(Base, TimestampMixin):
162
+ """Conversation model - groups messages in a chat session."""
163
+
164
+ __tablename__ = "conversations"
165
+
166
+ id: Mapped[str] = mapped_column(
167
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
168
+ )
169
+ {%- if cookiecutter.use_jwt %}
170
+ user_id: Mapped[str | None] = mapped_column(
171
+ String(36),
172
+ ForeignKey("users.id", ondelete="CASCADE"),
173
+ nullable=True,
174
+ index=True,
175
+ )
176
+ {%- endif %}
177
+ title: Mapped[str | None] = mapped_column(String(255), nullable=True)
178
+ is_archived: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
179
+
180
+ # Relationships
181
+ messages: Mapped[list["Message"]] = relationship(
182
+ "Message",
183
+ back_populates="conversation",
184
+ cascade="all, delete-orphan",
185
+ order_by="Message.created_at",
186
+ )
187
+
188
+ def __repr__(self) -> str:
189
+ return f"<Conversation(id={self.id}, title={self.title})>"
190
+
191
+
192
+ class Message(Base, TimestampMixin):
193
+ """Message model - individual message in a conversation."""
194
+
195
+ __tablename__ = "messages"
196
+
197
+ id: Mapped[str] = mapped_column(
198
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
199
+ )
200
+ conversation_id: Mapped[str] = mapped_column(
201
+ String(36),
202
+ ForeignKey("conversations.id", ondelete="CASCADE"),
203
+ nullable=False,
204
+ index=True,
205
+ )
206
+ role: Mapped[str] = mapped_column(String(20), nullable=False)
207
+ content: Mapped[str] = mapped_column(Text, nullable=False)
208
+ model_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
209
+ tokens_used: Mapped[int | None] = mapped_column(Integer, nullable=True)
210
+
211
+ # Relationships
212
+ conversation: Mapped["Conversation"] = relationship(
213
+ "Conversation", back_populates="messages"
214
+ )
215
+ tool_calls: Mapped[list["ToolCall"]] = relationship(
216
+ "ToolCall",
217
+ back_populates="message",
218
+ cascade="all, delete-orphan",
219
+ order_by="ToolCall.started_at",
220
+ )
221
+
222
+ def __repr__(self) -> str:
223
+ return f"<Message(id={self.id}, role={self.role})>"
224
+
225
+
226
+ class ToolCall(Base):
227
+ """ToolCall model - record of a tool invocation."""
228
+
229
+ __tablename__ = "tool_calls"
230
+
231
+ id: Mapped[str] = mapped_column(
232
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
233
+ )
234
+ message_id: Mapped[str] = mapped_column(
235
+ String(36),
236
+ ForeignKey("messages.id", ondelete="CASCADE"),
237
+ nullable=False,
238
+ index=True,
239
+ )
240
+ tool_call_id: Mapped[str] = mapped_column(String(100), nullable=False)
241
+ tool_name: Mapped[str] = mapped_column(String(100), nullable=False)
242
+ args: Mapped[str] = mapped_column(Text, nullable=False, default="{}") # JSON as string
243
+ result: Mapped[str | None] = mapped_column(Text, nullable=True)
244
+ status: Mapped[str] = mapped_column(String(20), nullable=False, default="pending")
245
+ started_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
246
+ completed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
247
+ duration_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
248
+
249
+ # Relationships
250
+ message: Mapped["Message"] = relationship("Message", back_populates="tool_calls")
251
+
252
+ def __repr__(self) -> str:
253
+ return f"<ToolCall(id={self.id}, tool_name={self.tool_name})>"
254
+
255
+
256
+ {%- elif cookiecutter.enable_conversation_persistence and cookiecutter.use_mongodb %}
257
+ """Conversation and message models for AI chat persistence (MongoDB)."""
258
+
259
+ from datetime import UTC, datetime
260
+ from typing import Literal, Optional
261
+
262
+ from beanie import Document, Link
263
+ from pydantic import Field
264
+
265
+
266
+ class ToolCall(Document):
267
+ """ToolCall document model - record of a tool invocation."""
268
+
269
+ message_id: str
270
+ tool_call_id: str
271
+ tool_name: str
272
+ args: dict = Field(default_factory=dict)
273
+ result: Optional[str] = None
274
+ status: Literal["pending", "running", "completed", "failed"] = "pending"
275
+ started_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
276
+ completed_at: Optional[datetime] = None
277
+ duration_ms: Optional[int] = None
278
+
279
+ class Settings:
280
+ name = "tool_calls"
281
+ indexes = ["message_id"]
282
+
283
+
284
+ class Message(Document):
285
+ """Message document model - individual message in a conversation."""
286
+
287
+ conversation_id: str
288
+ role: Literal["user", "assistant", "system"]
289
+ content: str
290
+ model_name: Optional[str] = None
291
+ tokens_used: Optional[int] = None
292
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
293
+
294
+ class Settings:
295
+ name = "messages"
296
+ indexes = ["conversation_id"]
297
+
298
+
299
+ class Conversation(Document):
300
+ """Conversation document model - groups messages in a chat session."""
301
+
302
+ {%- if cookiecutter.use_jwt %}
303
+ user_id: Optional[str] = None
304
+ {%- endif %}
305
+ title: Optional[str] = None
306
+ is_archived: bool = False
307
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
308
+ updated_at: Optional[datetime] = None
309
+
310
+ class Settings:
311
+ name = "conversations"
312
+ {%- if cookiecutter.use_jwt %}
313
+ indexes = ["user_id"]
314
+ {%- endif %}
315
+
316
+
317
+ {%- else %}
318
+ """Conversation models - not configured."""
319
+ {%- endif %}
@@ -0,0 +1,96 @@
1
+ {%- if cookiecutter.include_example_crud and cookiecutter.use_postgresql %}
2
+ """Item database model - example CRUD entity."""
3
+
4
+ import uuid
5
+
6
+ from sqlalchemy import String, Text
7
+ from sqlalchemy.dialects.postgresql import UUID
8
+ from sqlalchemy.orm import Mapped, mapped_column
9
+
10
+ from app.db.base import Base, TimestampMixin
11
+
12
+
13
+ class Item(Base, TimestampMixin):
14
+ """Item model - example entity for demonstrating CRUD operations.
15
+
16
+ This is a simple example model. You can use it as a template
17
+ for creating your own models or remove it if not needed.
18
+ """
19
+
20
+ __tablename__ = "items"
21
+
22
+ id: Mapped[uuid.UUID] = mapped_column(
23
+ UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
24
+ )
25
+ title: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
26
+ description: Mapped[str | None] = mapped_column(Text, nullable=True)
27
+ is_active: Mapped[bool] = mapped_column(default=True, nullable=False)
28
+
29
+ def __repr__(self) -> str:
30
+ return f"<Item(id={self.id}, title={self.title})>"
31
+
32
+
33
+ {%- elif cookiecutter.include_example_crud and cookiecutter.use_sqlite %}
34
+ """Item database model - example CRUD entity."""
35
+
36
+ import uuid
37
+
38
+ from sqlalchemy import Boolean, String, Text
39
+ from sqlalchemy.orm import Mapped, mapped_column
40
+
41
+ from app.db.base import Base, TimestampMixin
42
+
43
+
44
+ class Item(Base, TimestampMixin):
45
+ """Item model - example entity for demonstrating CRUD operations.
46
+
47
+ This is a simple example model. You can use it as a template
48
+ for creating your own models or remove it if not needed.
49
+ """
50
+
51
+ __tablename__ = "items"
52
+
53
+ id: Mapped[str] = mapped_column(
54
+ String(36), primary_key=True, default=lambda: str(uuid.uuid4())
55
+ )
56
+ title: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
57
+ description: Mapped[str | None] = mapped_column(Text, nullable=True)
58
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
59
+
60
+ def __repr__(self) -> str:
61
+ return f"<Item(id={self.id}, title={self.title})>"
62
+
63
+
64
+ {%- elif cookiecutter.include_example_crud and cookiecutter.use_mongodb %}
65
+ """Item document model for MongoDB - example CRUD entity."""
66
+
67
+ from datetime import UTC, datetime
68
+ from typing import Optional
69
+
70
+ from beanie import Document
71
+ from pydantic import Field
72
+
73
+
74
+ class Item(Document):
75
+ """Item document model - example entity for demonstrating CRUD operations.
76
+
77
+ This is a simple example model. You can use it as a template
78
+ for creating your own models or remove it if not needed.
79
+ """
80
+
81
+ title: str = Field(max_length=255)
82
+ description: Optional[str] = None
83
+ is_active: bool = True
84
+ created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
85
+ updated_at: Optional[datetime] = None
86
+
87
+ class Settings:
88
+ name = "items"
89
+ indexes = [
90
+ "title",
91
+ ]
92
+
93
+
94
+ {%- else %}
95
+ """Item model - not configured."""
96
+ {%- endif %}