fastapi-fullstack 0.2.18__py3-none-any.whl → 0.2.19__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 (36) hide show
  1. {fastapi_fullstack-0.2.18.dist-info → fastapi_fullstack-0.2.19.dist-info}/METADATA +10 -11
  2. {fastapi_fullstack-0.2.18.dist-info → fastapi_fullstack-0.2.19.dist-info}/RECORD +33 -35
  3. fastapi_gen/UPGRADES.yaml +16 -0
  4. fastapi_gen/prompts.py +1 -1
  5. fastapi_gen/template/hooks/post_gen_project.py +0 -1
  6. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/0027_user_magic_link_epoch.py +55 -0
  7. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py +24 -2
  8. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py +56 -5
  9. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py +81 -11
  10. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/knowledge_bases.py +3 -4
  11. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/org_integrations.py +2 -3
  12. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/crypto.py +11 -2
  13. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py +36 -4
  14. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py +19 -2
  15. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py +3 -11
  16. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py +5 -1
  17. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/agent_session.py +80 -10
  18. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/__init__.py +18 -0
  19. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/service.py +87 -26
  20. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/storage.py +25 -10
  21. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/sync_source.py +22 -2
  22. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py +52 -6
  23. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml +0 -3
  24. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py +52 -0
  25. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py +1 -1
  26. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_openapi.py +17 -0
  27. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py +18 -0
  28. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py +48 -5
  29. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py +47 -0
  30. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py +94 -0
  31. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py +0 -173
  32. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py +0 -140
  33. fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py +0 -53
  34. {fastapi_fullstack-0.2.18.dist-info → fastapi_fullstack-0.2.19.dist-info}/WHEEL +0 -0
  35. {fastapi_fullstack-0.2.18.dist-info → fastapi_fullstack-0.2.19.dist-info}/entry_points.txt +0 -0
  36. {fastapi_fullstack-0.2.18.dist-info → fastapi_fullstack-0.2.19.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-fullstack
3
- Version: 0.2.18
3
+ Version: 0.2.19
4
4
  Summary: Full-stack FastAPI + Next.js template generator with PydanticAI/LangChain agents, WebSocket streaming, 20+ enterprise integrations, and Logfire/LangSmith observability. Ship AI apps fast. CLI tool to generate production-ready FastAPI + Next.js projects with AI agents, auth, and observability.
5
5
  Project-URL: Homepage, https://github.com/vstorm-co/full-stack-ai-agent-template
6
6
  Project-URL: Documentation, https://github.com/vstorm-co/full-stack-ai-agent-template#readme
@@ -19,27 +19,26 @@ Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Topic :: Software Development :: Code Generators
21
21
  Requires-Python: >=3.11
22
- Requires-Dist: arq>=0.28.0
23
- Requires-Dist: celery>=5.6.3
24
22
  Requires-Dist: click>=8.3.3
25
23
  Requires-Dist: cookiecutter>=2.7.0
26
24
  Requires-Dist: email-validator>=2.3.0
27
- Requires-Dist: httpx>=0.28.1
28
25
  Requires-Dist: packaging>=24.0
29
- Requires-Dist: pydantic-settings>=2.14.2
30
26
  Requires-Dist: pydantic>=2.13.0
31
- Requires-Dist: pytest-asyncio>=1.3.0
32
- Requires-Dist: pytest>=9.0.3
33
27
  Requires-Dist: pyyaml>=6.0
34
28
  Requires-Dist: questionary>=2.1.0
35
29
  Requires-Dist: rich>=15.0.0
36
30
  Requires-Dist: ruff>=0.14.0
37
- Requires-Dist: stripe>=15.1.0
38
- Requires-Dist: taskiq>=0.12.4
39
31
  Provides-Extra: dev
32
+ Requires-Dist: arq>=0.28.0; extra == 'dev'
33
+ Requires-Dist: celery>=5.6.3; extra == 'dev'
34
+ Requires-Dist: httpx>=0.28.1; extra == 'dev'
40
35
  Requires-Dist: pre-commit>=4.0.0; extra == 'dev'
36
+ Requires-Dist: pydantic-settings>=2.14.2; extra == 'dev'
37
+ Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
41
38
  Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
42
39
  Requires-Dist: pytest>=9.0.0; extra == 'dev'
40
+ Requires-Dist: stripe>=15.1.0; extra == 'dev'
41
+ Requires-Dist: taskiq>=0.12.4; extra == 'dev'
43
42
  Requires-Dist: ty>=0.0.31; extra == 'dev'
44
43
  Provides-Extra: docs
45
44
  Requires-Dist: mkdocs-material>=9.7.0; extra == 'docs'
@@ -810,7 +809,7 @@ They also ship a ready-to-use **`.claude/` toolkit** that adapts to the options
810
809
  | **Background Tasks** | Celery, Taskiq, ARQ, Prefect |
811
810
  | **Billing** | Stripe subscriptions (seat-based), credits + usage metering, invoices, Customer Portal |
812
811
  | **Caching & State** | Redis, fastapi-cache2 |
813
- | **Security** | Rate limiting, CORS, CSRF protection |
812
+ | **Security** | Per-plan sliding-window rate limiting (per user / org / IP), CORS, SSRF-guarded webhook delivery, httpOnly + SameSite session cookies |
814
813
  | **Observability** | Logfire, LangSmith, Sentry, Prometheus |
815
814
  | **Admin** | SQLAdmin panel with auth |
816
815
  | **Collaboration** | Conversation sharing (direct + link), admin conversation browser |
@@ -1315,7 +1314,7 @@ Select what you need:
1315
1314
  ```bash
1316
1315
  fastapi-fullstack new
1317
1316
  # ✓ Redis (caching/sessions)
1318
- # ✓ Rate limiting (slowapi)
1317
+ # ✓ Rate limiting (per user/org/IP, Redis or in-memory)
1319
1318
  # ✓ Pagination (fastapi-pagination)
1320
1319
  # ✓ Admin Panel (SQLAdmin)
1321
1320
  # ✓ AI Agent (PydanticAI or LangChain)
@@ -2,7 +2,7 @@ fastapi_gen/__init__.py,sha256=oK2gEM45SpSYBb-Itgr0GII88-5qfB0kmNkj90FkvQw,257
2
2
  fastapi_gen/cli.py,sha256=ZpwdniivCiP1bVP54xW2H5hSaQzqUuu7ktfFxBIrNg0,60765
3
3
  fastapi_gen/config.py,sha256=zZaIF5erTvs9Cv1cONnMMQ9voI6OLSz0-9d-eztYRDU,44147
4
4
  fastapi_gen/generator.py,sha256=vMZ18ANuhCKva2pDklIIAZO7bkwX-ehvPzmFV0ND3vw,8441
5
- fastapi_gen/prompts.py,sha256=bLosSQJHGitLdfxQc449s2bl045a2up4kJ1noZ1r60I,66482
5
+ fastapi_gen/prompts.py,sha256=YNPNCiBmOC7-rlpH79tMYsyLSJoPzpmFAnOQsR-sjfw,66494
6
6
  fastapi_gen/upgrade/__init__.py,sha256=-JlxOkKPW-rAbldIj2V4czXN3RGoP_tSm2hB0U8OO8M,640
7
7
  fastapi_gen/upgrade/_render_worker.py,sha256=dAq5pmWJPdB3VRujFSYSh9tQ8z7klow6rCucgXEH6rE,1363
8
8
  fastapi_gen/upgrade/classify.py,sha256=19veC5-h88WX8gf8MWHR0c8e9-G7Am5F6-TxI8NhE30,5745
@@ -17,10 +17,10 @@ fastapi_gen/upgrade/rename_guard.py,sha256=ap0ds5k93Hh6hhQhOxrDPsh-utTE1BeBYysrT
17
17
  fastapi_gen/upgrade/render.py,sha256=PmEm7BPmEfUK29RcVgPN9PttTX7oHeWnDgf_vgGja1s,5497
18
18
  fastapi_gen/upgrade/report.py,sha256=49v9kq2tkbeqqbdH-kRIM3kccaFfPVfjhi7Tm5cCVvE,3802
19
19
  fastapi_gen/upgrade/runner.py,sha256=msnj2RYii6low6gPeZ4TRMlL01Hq8a0zJyLBzPotxCI,19302
20
- fastapi_gen/UPGRADES.yaml,sha256=DciwcDB-T5LBX-PVTLBqSrm3t6gVL3Mhm5McBkOkORo,1524
20
+ fastapi_gen/UPGRADES.yaml,sha256=TaVbh-X3gstDtDrAZ7HhT1Dojp0qf8BNfVWSmehL4Lw,3682
21
21
  fastapi_gen/template/VARIABLES.md,sha256=9CcimCDgXLshogzr1pjRYUTTmECCL9NAb1OzDFNTfgw,34322
22
22
  fastapi_gen/template/cookiecutter.json,sha256=xikYtcbOLG4VOwZXAk-0acbELW8jXhHAGEg4kGEgRdg,6337
23
- fastapi_gen/template/hooks/post_gen_project.py,sha256=o79caVy6BKSjgK2rpEYlLx0TEA3kAOg03yPDePuGfkY,58054
23
+ fastapi_gen/template/hooks/post_gen_project.py,sha256=RWwUmM6bVZnwFozyi_x1KLS2BHWAet0B0up6zWFZmd0,57986
24
24
  fastapi_gen/template/{{cookiecutter.project_slug}}/.gitignore,sha256=yC3ZUp8Txp_1TPDabDlz4pGa2p1cNwr49de0dCcctOw,1515
25
25
  fastapi_gen/template/{{cookiecutter.project_slug}}/.gitlab-ci.yml,sha256=dZh-VSLL5beOtj8_UxBbD86sBdE9ubTpmWvMcrRqr-M,4651
26
26
  fastapi_gen/template/{{cookiecutter.project_slug}}/AGENTS.md,sha256=HqhXNdBq1ZXlFJCC4V3CY8DorTkZkwFJcvv2O77Pwk4,2454
@@ -60,7 +60,7 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.env.example,sha256=H
60
60
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/.pre-commit-config.yaml,sha256=22sYeJOrYywl_5PWgPzCyIwhpqmymIDIzx2hlBRQ-yI,681
61
61
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/Dockerfile,sha256=CKyuUKwt41LHIH9VB28TEzes5_TQkUEo9rIBjE3v8a8,2184
62
62
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic.ini,sha256=vncO8XHTAW1ospWsdvtXD5PyMhp91CJmdVfv-6TakAY,737
63
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml,sha256=nWgclrugZfrxqEA1dgSjMNTMNfCU11Qf0Vs9AjRnpHE,14780
63
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/pyproject.toml,sha256=dIs_3KaQ7P1FqjTjd8WtvVX_ZHNxVKwbfieS3ByPGJA,14701
64
64
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/env.py,sha256=ifDazpmfYDUI0KGxaGcAgJbz6HNzu9bQYB6li88OMcU,3163
65
65
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/script.py.mako,sha256=ZCfgWrEMJRDF_WF_GRGptwAIJI6YnAditBG1axwAP9Q,700
66
66
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -91,9 +91,10 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/0023
91
91
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/0024_create_webhook_tables.py,sha256=HExnIhQAJXNCe1vxhagIhUr0BeBBv2MTBadQEY4GtY8,3233
92
92
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/0025_message_thinking.py,sha256=8Wi3zcAXADP6WbjuB-Na_Y_OZTx6DDETkrZAVrNEdl8,727
93
93
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/0026_create_mcp_connections.py,sha256=ADbedBphmfFnRiZ7fssMjD2fgfqz6YIUf7F2DguKm68,3188
94
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/alembic/versions/0027_user_magic_link_epoch.py,sha256=tbI74ZtMv0UVT1sUiAzCZSd6w_efFVh-8-E_bypdt00,1411
94
95
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/__init__.py,sha256=vNnuyVXC5oZ3yq46meWLNhkUYlMgod70TgvWyvgNhBk,68
95
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py,sha256=QxVibclQNYtROGtf00Lsb5dlskvs4yLZ7SaEz6vf-IA,12990
96
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py,sha256=CcUQKjWvANuNCRIS_vEry6ZglCs1wIwD76HkbMa3BHw,18948
96
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/admin.py,sha256=jK6iIS4OlsSBID2xZOYWZCEndpSAhxwclc_lceZKIUI,14043
97
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/main.py,sha256=9MqX6pAhFf5Qwq4zF53c83qlab4HQrVLHFYwfpjFXPo,18584
97
98
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/__init__.py,sha256=DP_Hrjq0WlyuWhrkplLKmS3pVsoGZgFQgXhAcsjqBlQ,1889
98
99
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/assistant.py,sha256=NXZTvQBn7iaqQVcXCUwAusSKx1PmBpcDiYl2uUa6hKA,22196
99
100
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/deepagents_assistant.py,sha256=vziHT47EmLWSh7RJWCAUHGu5orxm1PrINwLEc7OJJN8,30372
@@ -113,10 +114,9 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/rag_
113
114
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/research.py,sha256=p3j5K5FdjJ3JyktyEHVRj_P0DQBV3KJAha7lDyLCrIk,168
114
115
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/agents/tools/web_search.py,sha256=CyRc7-rklcMl6s_9Cnl1xLDcBNm8Q3mMiEu3bYKagQk,3177
115
116
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/__init__.py,sha256=NeESK8RjcyjY9KL1c9H2jIw34RpWmtlHgd8rhPBuolo,18
116
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py,sha256=zd9VVLpvmbYqNb6ShyhwiM_pt3ByQkBgnChQOH99aJA,25135
117
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/deps.py,sha256=DbimT-gx2At1hdnS0SIxOmiZWmJDkLxCDOHK5euJkoQ,27384
117
118
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/exception_handlers.py,sha256=FGDbVsO-UzvWZtcnLW6joZXPZahZc9O13p_tqzlhF6c,3453
118
119
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/router.py,sha256=8E0e0Oef6KrB6raDgrV0Lj-tPKRiQ-5UlGM3yGnz50Q,233
119
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/versioning.py,sha256=O5FQre_y1hwJGsbREeKbRKoxEWUVhGNg7gIC76FqyPo,5906
120
120
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/__init__.py,sha256=wIJD_xpViBStOBb4iWBX_hxdPfSKJyAGlcOw81NTWXo,207
121
121
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/__init__.py,sha256=WeCCXp1dETT-BsVXFzS86wlzXp6YScgij2ahudY2gBg,7292
122
122
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/admin_conversations.py,sha256=Kmzwl-ve6jbukTa5WNQIydOOR1cmIvXWLtamXS9Oj4E,2612
@@ -124,7 +124,7 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/adm
124
124
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/admin_stats.py,sha256=z6xVC9yac5jcGpt5QK91VdzpgYap9ETWgCx2M6yKITM,1177
125
125
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/admin_users.py,sha256=dcg36c-q-1qb7H1m7gQOF52SHclfeEyCqbqUpjUrxMw,3924
126
126
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/agent.py,sha256=muXxu5PJnZivVV_fgci6_iRvibg9ZyKwuVOtbJT37Wg,10573
127
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py,sha256=c3Vy2i7eIeQhwftIZvThoomjMS8NvQ150XQH52KDA_g,8160
127
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/auth.py,sha256=owJbU0Pk3474cXY0m7vpXDE1RaSFNvbhfO1MrZhu388,10575
128
128
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/billing.py,sha256=NNEGZBkqC9mBc1eCQRyrVSbDPu00H8jwo1zq_DBoPWY,7533
129
129
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/channels.py,sha256=Zr9m3GqWNNoBIGps4rmNBf4a5ha8jEuzNZSPMRhNPyE,3512
130
130
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/contact.py,sha256=o7NDT4ZaHem3Bdj4aVL3oOhUT9owWCIErttJ68TmmPU,722
@@ -133,13 +133,13 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/fil
133
133
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/health.py,sha256=pGvisRQ8dLK6jbIpm5Q2hxvWq6VYi0BF_EFN6RWnvVY,8329
134
134
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/invitations.py,sha256=ZN4SKxzw14rIovpJAj9TkSbfpedo56zW2s9PfJq7FU4,2978
135
135
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/items.py,sha256=43S9OPXxAxrk_F10toYeLM0MRW_O37-w-0CQ8JAwDbo,2285
136
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/knowledge_bases.py,sha256=Wu3MzcI7xJWI9A2uRmhskYfOzeLDu-Hfuxdo8gxqD2o,14219
136
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/knowledge_bases.py,sha256=QQolJKIDUjqpVoBvdn1jrfnmBRKc7nRmoHiMMo2J6aI,14155
137
137
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/marketing.py,sha256=QG_CjH8_xk9Ii_D0Vjxu4li0y2pHvulUmB8X7n2l58o,750
138
138
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/me_mcp_connections.py,sha256=HTCzfTqkLTjYzfMzNFYlvXFAArJomTjZB93eMBBgx0w,4660
139
139
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/me_slash_commands.py,sha256=cCQP6JS8LRaTazgk2K0R4EASZSop1iCK34CAG3Fc3Kc,2685
140
140
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/members.py,sha256=5_Tfwc8s8JqTZUk5XMJ4HAGwEDSsehkrk73VSHUvQjQ,3329
141
141
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/oauth.py,sha256=Zz_fNrh2xNwRpoGdqOCGeZBPf4TcjMm0cAkq0I80x_U,2862
142
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/org_integrations.py,sha256=misA4_Jll5QUyHUBT6RaBgjkWe24vEK9bL-r3EZjznI,3972
142
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/org_integrations.py,sha256=uAAZT2WKhMs8maTBacAWfE8-o-8eFh_K9ovD7ZLcJis,3919
143
143
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/organizations.py,sha256=Zl3bJPy-RfpluSgadeStrp_KodVpLQKmUPkoFkXiXxU,4992
144
144
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/projects.py,sha256=d9l5HZ5wJl_MGnIvEJss_gXomxUR3XYLXB-grLV_81s,4565
145
145
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/api/routes/v1/public_demos.py,sha256=XP6KDu2U6LaJKydkuE5fYjO1PgggGrQVU9j1a9VRq9M,1538
@@ -164,16 +164,14 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/audit.py,sha
164
164
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/cache.py,sha256=VYZfvIdLdtb1Y8h09IoxqPkDyG8pZtY96hR15Hff8YM,626
165
165
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/channel_crypto.py,sha256=VG98BxIMoXL_6U6B40gCX_p8xHSoOTBPre3zCclVyQs,1608
166
166
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/config.py,sha256=HnVa4gZwadFI8x9bhUvCdnPGCPbscZB4_gEGACMhgKM,28332
167
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/crypto.py,sha256=Uv5x1umwjsvdEqfA5zQmolcwtTA1cmbuwRIkATZkYqw,1295
168
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/csrf.py,sha256=5tXguJXdMr8rs2TYTEyvUkY4r6nxcQa1ZrA8E3HPon4,4575
167
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/crypto.py,sha256=1XM09Vxf4yYzU-wiZAK4lN4Wy4j7aLTbPjbeTURnIfE,1760
169
168
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/exceptions.py,sha256=DRMaSCDwajxldiC530gJLHbsg4NeqdYdat7zBq5i4IQ,3000
170
169
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logfire_setup.py,sha256=Aq6k4FY7wrFUwFMPmTcVbq_VNfNdYJ5FOs5vOCsI1EI,1773
171
170
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/logging.py,sha256=7TFYXXeNAa5UnNLff2K-kwZFqi_VJ4jILMtLpm8H6Sw,2651
172
171
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/middleware.py,sha256=hYEOf45mYc9PRrayiO3XOd0cdLM2GYYS76o1Jiv6tUQ,3570
173
172
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/oauth.py,sha256=YtyJBlHY6qsond8aKOrg0S7OyKA22LZ7RN8QWi8G8Tg,571
174
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/rate_limit.py,sha256=cdn1M1JeoNPLiWkWKa5MzXDnpbUa403PTF3n4TioGAg,1435
175
173
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/sanitize.py,sha256=VuvNiccu2gErxVDmVAJ04E_oy-KxAzTLyujVaO2XCag,11567
176
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py,sha256=yF8v5VFvT3FiUsIeih_fc1RVO43hRPy4eNKb9Uh3UBQ,6380
174
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/core/security.py,sha256=nXjXx1MN3mR_mqZvBEZQbSOdFV8hM1oJyCx8FfJ2ofc,7661
177
175
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/__init__.py,sha256=VRbMV3VsQxDygLTek0L9_q1v1T_L9MjfGFobSUa2FHg,284
178
176
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/base.py,sha256=TWjarV2D4yIKC7w_e8gR0v6hUE94cBnl0bSa8GYp56s,2108
179
177
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/session.py,sha256=4aGrpl4JQDQVu5kOVQnI2dA4ObbJY5kOZYD94b5afpQ,2991
@@ -200,7 +198,7 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/stripe_
200
198
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/subscription.py,sha256=0odUi8jukbdbuhia74wZG4ARCog-_Ie2bjWusThm5AQ,5138
201
199
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/sync_log.py,sha256=6xnp8USm0oizrzMtZWAuZEppAlbA7k7C7wZfwi-iBFQ,3683
202
200
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/sync_source.py,sha256=BcY3gwEcwvPlRm4H2n2bVuXOEt9QkkSapwHd81z8ot0,4080
203
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py,sha256=JTHHWB41LnR8JYvy3or5JApmAygdY8LIhf0lRt6MtkA,5966
201
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user.py,sha256=PaFft2fL4ifrb0vK1hGgCkOmmRYRg1ddXx97dmON0Bw,6877
204
202
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/user_slash_command.py,sha256=ZN71Ry1RcIlG1eUoahySsJW1Q7rxm3gKbeoiVnskspQ,4109
205
203
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/db/models/webhook.py,sha256=kl-2FRe0ineDl8mtI9wT1SFnQwqvZtebbfplfE3QtNI,5697
206
204
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/repositories/__init__.py,sha256=Wog9087BD1uhNzf-epXUo7YA5M8QeTuADRYcjL-9C4w,3558
@@ -253,12 +251,12 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/sync_sour
253
251
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/token.py,sha256=fle_Sx9A4c9e4ilLIpuxzLlwioRkkQJx2MhzgKrB7I4,618
254
252
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user.py,sha256=uu6YTYy-Y009d67kkBFV3q8UexTt1Ww7s1JXATPRB4Y,2425
255
253
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/user_slash_command.py,sha256=94XnNqmSsaCPBhZIkkIl6yht_DoMisk6AN_CquJL40c,1887
256
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py,sha256=Dw3HLj_8iXAgn72tx2P6MitBAsfy_LqRDjsLXwjPY3s,1889
254
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/schemas/webhook.py,sha256=VRwBD9l_lBfnPjQxp8kg4DOLyd6FCAUj5BZjoXZN0oI,2166
257
255
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/__init__.py,sha256=2eaFa3riNnh9BVf4A6i7ChK1yqCOA8VyTg1zeL2tYy0,523
258
256
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/admin.py,sha256=FzwzW6FpIx1Ciwj_9lCbWMzi-EWv3v2-J8Sopi9LiZU,5576
259
257
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/agent.py,sha256=nL7gsIBIHNsyePQHUBSWU5sO9jotfOkCXgE2T9t9hPY,12064
260
258
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/agent_invocation.py,sha256=BOUuHp8QVXYmQTX30vQw4Wl2U9D3QsUA3sNrT7CMaRw,16486
261
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/agent_session.py,sha256=z9vK8-CvOQMmQgiuYhGIJFsCkGA1L_9kp4mo27DDUlE,111980
259
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/agent_session.py,sha256=yLT1T0nPHnkurzyFSbFmQ9RNzyQW20T2H9rh6gGXIf0,115846
262
260
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/anomaly_detection.py,sha256=uKPCzzdeyDSUbgkD7VwFIubEHhoU1LEoOp3R1_F0SlE,3112
263
261
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/channel_bot.py,sha256=R1tfQQcNxHKZ9C3yOD6Hh4TANY77FL4XNic4Rf40qWo,6383
264
262
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/contact.py,sha256=AuML8RHIozZkchZlNMjHsZhLWpTMxukfy1xk6mak3Bk,1697
@@ -281,9 +279,9 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rag_stat
281
279
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rag_sync.py,sha256=7SJvrszd-z7k1jMz67Ez6gfbS45oxxCePa9WHZh_EVw,5958
282
280
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/research.py,sha256=APGywGFljVcTi4D9LLBTmVYICe2VfyE2QnjKS2ppaHk,12154
283
281
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/session.py,sha256=mABDVV3lMvmJsprr-y_XSMQ8hTb4PIEnK_bn5jasUUw,4103
284
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/sync_source.py,sha256=59RUGb_Gl3J6fy2wQpXD2T2RyUG7lp4Mpkcv-fQxN9E,11319
282
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/sync_source.py,sha256=geoKqmIjDgxAzS5Jmm2qW7fdAl9i_1ZgfiGFMNf951k,12199
285
283
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/usage.py,sha256=SUNAEnnOZjXr_PXoFGjfkMjAw7aB5FvIHReeAVI4wx8,3756
286
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py,sha256=y50P1A2CoQplNCFddxc7npWmY-aDuKJnWsWOf9CUTbY,13274
284
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user.py,sha256=OcOiIm2tIChbpb0FqvsGU1pMcXWs6PtParnoe3dVSaU,15172
287
285
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/user_slash_command.py,sha256=t1-IIExupr-pvhCt_JjmiLrWfzMEzR2cDcLfhfPVRCQ,5427
288
286
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/webhook.py,sha256=RCT96wAQDnr8CUG9_gzXYxi-Av3BXzjbl0BnxNY1qWw,7500
289
287
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/billing/__init__.py,sha256=t953_p9xlo9jNiflpiQBBiht-wWXV-RJAHi75Dp7w-0,448
@@ -332,10 +330,10 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rag/sour
332
330
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rag/sources/base.py,sha256=KGX85J0pHMX9j8PTJm64gYRckV53t3mGT3IYDj6J-Tg,3781
333
331
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rag/sources/google_drive.py,sha256=z4cComs7y9dKjm1VYyiLguB-t-JDJ-SQ9Q74_2c8vQ4,5001
334
332
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rag/sources/s3.py,sha256=mqW6lCmG2rsCal0P_IkL_C80SiTGkTNNBCK0nva7lS0,3055
335
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/__init__.py,sha256=ItvkNarmfp_ttog5NybcyzxgAuVc12sTv2TZeMsuZCc,165
333
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/__init__.py,sha256=2J-r2Z8IywpsP5IUKjAuWzJ1CaH3x8M56cmYqtEB_dI,600
336
334
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/rules.py,sha256=qhWTMlnGOrMlt3xb2mUwi_wsk4ND91mTMfjuaYfbqUM,1767
337
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/service.py,sha256=eTHF-abm5RGdFZr-ajV03qO0N5kyvTF63wLx0xK8xLo,4880
338
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/storage.py,sha256=pfnKQTUiKOpwfOYMCfm08iWyAVpqZ3yLgEEoHYVLxnM,4549
335
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/service.py,sha256=kjx6CMorIYWoEh1HPpJ_IdUNI4r0SyjMZBAtup0p5RU,6769
336
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/services/rate_limit/storage.py,sha256=-YN2F6OFikcU6jHxCUzRX6SnRSN_I27Lx_aKcl9aTHM,5422
339
337
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/__init__.py,sha256=wPhtHZI_FCMIJOeVnqX4BR85311K3ESYszlRZRadVYI,156
340
338
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/arq_app.py,sha256=re-StDERu9aB_YzMOBUoSOokxIBV6pb-EfATFKFeiZM,2594
341
339
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/app/worker/celery_app.py,sha256=s4sHn9XBeIdknD6pKUlvJjI4Eg3aE_eHr3cLl0_WUnQ,2177
@@ -354,14 +352,14 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/__init__.py,sha25
354
352
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/cli/commands.py,sha256=T8GO9T32U5ceUMk-1kB8mu9884xQbfjlm_2fW7rvGUs,9349
355
353
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/scripts/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
356
354
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/__init__.py,sha256=xqsJPT1QR2TH_5GYK8tiHp-Lb29chxGsr147MWoLAZw,21
357
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py,sha256=WtfER2c2jjsJXP7t1T3MJJkGH3SfUg5depLf0MQX4kA,3351
355
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/conftest.py,sha256=2k-vyUbgGXcQdgIm3eEAgznNnzBP90Lfbuf-q5Jx_V0,4065
358
356
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_admin.py,sha256=hLh04pv85EmS8CURqXWpFQw6xOiN217da8EJIw9n0Io,29588
359
357
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_agents.py,sha256=SyR_iCoMzOoJl0vvcAp68rUbYvymXnCdGjt0zRjbrn8,12424
360
358
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_chart_tool.py,sha256=RJ2E6Ww0pvVLypE9QJtifV90Z7Pap2I2VZET2NkdCcE,4706
361
359
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_clients.py,sha256=qKU2z4YVQMWdnNE9aH5lnACsdzQ1G4fcE9_sK8x7K2U,6282
362
360
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_commands.py,sha256=G7pRJgk0pDQRBIFUIhi0bbDnujwKcWCjCP9Q1D9TzYA,4894
363
361
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_conversation_kb_toggle.py,sha256=ZDlX1lMuLl4q41g0f8IE0oPt3lbca4an_8c19Z1xyo8,3888
364
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py,sha256=7U1k_x36xxou6Mtenb7gx-ZPASpcy4zbEUY85WqwmV4,4239
362
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_core.py,sha256=dF_RgCNrHnI3PqhOVqqcYg_XgcDPCmkR_YWw6POCngw,5973
365
363
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_fetch_url.py,sha256=ZnPxBUYH6KyAWcQf1MTEQEUvb7tpDhXcmbVKRLR2Xuw,3595
366
364
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_kb_scoping.py,sha256=fEV6jEaPtW5oNyRBDAA_S2jHS2QyxXWmOG-EKhYD7Hk,6573
367
365
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_mcp_connections.py,sha256=bXtouBih2krDRw0Kb-kxvRJuHvI_5O3c-VRzCfif4Ig,34801
@@ -369,8 +367,8 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_migrations
369
367
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_rbac_teams.py,sha256=ryLOK_4YMtSBfRqeVFEnvcQXoGkQEpDrX0OAsCmm-v8,7037
370
368
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories.py,sha256=uQHtc5I5OZpWnO7rC1Y8C7uw1WZWtG_HNWwxpI2OhtM,1286
371
369
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_repositories_teams.py,sha256=bIvtIATkynFWyvi81zXphiVyFf1cWu6qgRiB-EQzgMo,9675
372
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py,sha256=6YJUtT9HqIkhcyAEmQSIiYNunNVC_hmsrfjscmdON-k,3752
373
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py,sha256=TvRjbkimtbG6ODadx4V4O8ISouboc5ze8567e1IgTJw,9340
370
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_security.py,sha256=Hjs9Qzrqgqh2LDTurntiJhg3nNFVf5d5J5gZXyE6QwA,5615
371
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services.py,sha256=RCjohaKjN9FR6gG7KsHf3-vm51T4XIA5ORN18vNMVbY,13067
374
372
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services_conversation.py,sha256=d9cn9w86kjlxPbO1vBCECBHPIYb75S5jvcYUtU14qkU,35129
375
373
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services_members.py,sha256=YTZDqwSfhZPuZIK6JSpYZa8tlr1PAvH2QpBhGxWrk-I,8812
376
374
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_services_organizations.py,sha256=LqguSbJVPhzVq3bHQZC37CNVF4MbQ93iDll0Vx28nV4,6277
@@ -380,11 +378,11 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_tenant_iso
380
378
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_web_search.py,sha256=rGF8rftjC80Ujke0Ahw--3HfB7k2lJA5BkpPFKOKC2A,3119
381
379
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/test_worker_taskiq.py,sha256=1SsCCaZ-gBzakWdKvxRChl3PY0beocV17u8Rv1GPuUc,1557
382
380
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/__init__.py,sha256=WV9U5TSWbUYHVA2F3CD7gfod4RdX8EM1s4ewEnwszr0,25
383
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py,sha256=5hpPUvvwqmD80keuZ2rWvb7UGYWOwpa6vYi5dLC34hY,7373
384
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py,sha256=QQh6E6QXdrgZRP-ym0jIroy50x4aYrVadrohTzu5k9o,4617
381
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_auth.py,sha256=vSS2m9Z-IW-c8eOlEGcD9A9hpK6lbLMQc4AUXMcs-T0,9306
382
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_exceptions.py,sha256=X42FJacw5Sg32gufnNoreQOEDyPNuS3QhxtWlYRKEmQ,4657
385
383
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_health.py,sha256=OV3lPybEfyEDNrhmiCe4WMKsm8KxrXFKFBWoceDgrsc,2741
386
384
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_metrics.py,sha256=nikYPuAynnvHWSRFWsuFuqBYY3jyN5sJH2dgiONaVdA,1835
387
- fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_openapi.py,sha256=bt6OOz1DWhynKcCUxFqkF7FQuKAhBtDPAvUgiS6tu0s,3598
385
+ fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_openapi.py,sha256=V2HBIgRcuwk-Y7nwJK0tkKE_MtQpV6xBY66-TKRYCJk,4378
388
386
  fastapi_gen/template/{{cookiecutter.project_slug}}/backend/tests/api/test_users.py,sha256=7QXy6Z0BflemKFtyViOIUVG3MjDpJi-FbLx4CWK9kBk,7231
389
387
  fastapi_gen/template/{{cookiecutter.project_slug}}/docs/adding_features.md,sha256=oShZqruknW6iYaMLutNvSpXYQP3cmZGg3OuDVaxrAZY,6851
390
388
  fastapi_gen/template/{{cookiecutter.project_slug}}/docs/architecture.md,sha256=Lg8RzpLDO9tA-zVE3lFnllnTkW-FvQwc7-XMseneTq4,11806
@@ -916,8 +914,8 @@ fastapi_gen/template/{{cookiecutter.project_slug}}/kubernetes/service.yaml,sha25
916
914
  fastapi_gen/template/{{cookiecutter.project_slug}}/nginx/nginx.conf,sha256=2bWaxnv_rN4driEdtWgVz9eVfoKhB_Ohd_G8eOC3P3M,7619
917
915
  fastapi_gen/template/{{cookiecutter.project_slug}}/nginx/ssl/.gitkeep,sha256=o_WQQOgVGOLttT48x0EXSC4BbbDFemApFpmwhybcVUU,617
918
916
  fastapi_gen/template/{{cookiecutter.project_slug}}/scripts/export_demo_html.py,sha256=H1tbIfr1xrh2pRuoy5NwvAOYEu0S2vgnw_XsKNMKXeM,10414
919
- fastapi_fullstack-0.2.18.dist-info/METADATA,sha256=LJac85BvO3ex5oIo2gj3B0A2qB7fLY8e1s855n2Jb7c,60593
920
- fastapi_fullstack-0.2.18.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
921
- fastapi_fullstack-0.2.18.dist-info/entry_points.txt,sha256=s9JXrISZp8LMYJGeVofOAd1wPTzpq-jwjSgSf4hWzjs,59
922
- fastapi_fullstack-0.2.18.dist-info/licenses/LICENSE,sha256=bL4JuK_rcA8y__Gg7PEuTs3g2Qf6VvSz2w2Jajd6nVU,1063
923
- fastapi_fullstack-0.2.18.dist-info/RECORD,,
917
+ fastapi_fullstack-0.2.19.dist-info/METADATA,sha256=8uC2UYeJGbN1opmjhuTxoVSVBm_c9qNedB1KbtGd2mM,60801
918
+ fastapi_fullstack-0.2.19.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
919
+ fastapi_fullstack-0.2.19.dist-info/entry_points.txt,sha256=s9JXrISZp8LMYJGeVofOAd1wPTzpq-jwjSgSf4hWzjs,59
920
+ fastapi_fullstack-0.2.19.dist-info/licenses/LICENSE,sha256=bL4JuK_rcA8y__Gg7PEuTs3g2Qf6VvSz2w2Jajd6nVU,1063
921
+ fastapi_fullstack-0.2.19.dist-info/RECORD,,
fastapi_gen/UPGRADES.yaml CHANGED
@@ -18,6 +18,22 @@
18
18
  # manual_steps:
19
19
  # - "Run `alembic upgrade head` (new billing tables)."
20
20
 
21
+ - version: "0.2.19"
22
+ removed:
23
+ - "backend/app/core/rate_limit.py"
24
+ - "backend/app/core/csrf.py"
25
+ - "backend/app/api/versioning.py"
26
+ breaking:
27
+ - "Delegated auth (--auth-mode delegated) no longer generates the local credential endpoints: /auth/login, /auth/register, /auth/refresh, /auth/logout and the password-reset and magic-link routes. Only /auth/me remains. Any client calling them against a delegated deployment was receiving tokens that get_current_user rejects."
28
+ - "create_password_reset_token(subject) now requires current_password_hash, and create_magic_link_token(subject) requires magic_link_epoch. Both bind the token to state that changes on redemption, which is what makes the link single-use. Custom callers must pass the new argument."
29
+ - "The slowapi limiter in core/rate_limit.py is gone. It was never attached to a route and enforced nothing. Rate limits now come from services/rate_limit via make_rate_limit_dep / make_anonymous_rate_limit_dep, and slowapi is no longer a dependency."
30
+ manual_steps:
31
+ - "Run `alembic upgrade head` — 0027 adds users.magic_link_epoch, which the single-use magic-link check reads. Without it, sign-in-by-email raises on every attempt."
32
+ - "Rate limiting is now actually enforced on /auth/login, /auth/register and the password-reset and magic-link request endpoints (default: 5 per 15 minutes per IP). Load tests and smoke scripts that hammer login will start seeing 429s. Override per plan via the plan's features.rate_limits, or adjust DEFAULT_RATE_LIMITS in backend/app/services/rate_limit/rules.py."
33
+ - "If you generated with --rate-limiting and no Redis, limits are counted per worker process, so the effective limit is multiplied by your worker count. Enable Redis for a shared window."
34
+ - "Custom routes that need a limit: add `dependencies=[make_rate_limit_dep(RateLimitCategory.X)]` (authenticated) or `make_anonymous_rate_limit_dep` (pre-auth). Nothing is limited by default beyond the auth endpoints."
35
+ - "If you wired app.state.limiter or @limiter.limit from core/rate_limit.py yourself, those imports are now dead — port them to make_rate_limit_dep."
36
+
21
37
  - version: "0.2.18"
22
38
  manual_steps:
23
39
  - "Rebuild the frontend image: `docker compose -f docker-compose.prod.yml build --no-cache frontend`. NEXT_PUBLIC_* is inlined into the browser bundle at build time and now comes from compose build args, so an image built before this upgrade still has the old ws://localhost URL baked in."
fastapi_gen/prompts.py CHANGED
@@ -450,7 +450,7 @@ def prompt_integrations(
450
450
  checked=True,
451
451
  ),
452
452
  questionary.Choice(
453
- "Rate limiting (slowapi) — optional Redis storage",
453
+ "Rate limiting (per user/org/IP) — Redis or in-memory storage",
454
454
  value="rate_limiting",
455
455
  checked=True,
456
456
  ),
@@ -342,7 +342,6 @@ if not enable_caching:
342
342
  remove_file(os.path.join(backend_app, "core", "cache.py"))
343
343
 
344
344
  if not enable_rate_limiting:
345
- remove_file(os.path.join(backend_app, "core", "rate_limit.py"))
346
345
  remove_dir(os.path.join(backend_app, "services", "rate_limit"))
347
346
 
348
347
  if not enable_oauth:
@@ -0,0 +1,55 @@
1
+ {%- if cookiecutter.enable_email %}
2
+ """add users.magic_link_epoch
3
+
4
+ Revision ID: 0027_user_magic_link_epoch
5
+ Revises: 0026_create_mcp_connections
6
+ Create Date: {{ cookiecutter.generated_at }}
7
+
8
+ Magic-link sign-in tokens are JWTs, so they cannot be revoked. Each issued
9
+ link carries the epoch in force at issue time; redeeming one bumps the epoch,
10
+ which invalidates that link and every other link outstanding for the user.
11
+ Without this a leaked sign-in link stays usable for its whole TTL, and stays
12
+ usable after the legitimate user has already signed in with it.
13
+ """
14
+
15
+ import sqlalchemy as sa
16
+
17
+ from alembic import op
18
+
19
+ revision = "0027_user_magic_link_epoch"
20
+ down_revision = "0026_create_mcp_connections"
21
+ branch_labels = None
22
+ depends_on = None
23
+
24
+
25
+ def upgrade() -> None:
26
+ op.add_column(
27
+ "users",
28
+ sa.Column("magic_link_epoch", sa.Integer(), nullable=False, server_default="0"),
29
+ )
30
+
31
+
32
+ def downgrade() -> None:
33
+ op.drop_column("users", "magic_link_epoch")
34
+ {%- else %}
35
+ """add users.magic_link_epoch — skipped (enable_email=false)
36
+
37
+ Revision ID: 0027_user_magic_link_epoch
38
+
39
+ No-op placeholder so the revision chain stays linear when email (and therefore
40
+ magic-link sign-in) is disabled.
41
+ """
42
+
43
+ revision = "0027_user_magic_link_epoch"
44
+ down_revision = "0026_create_mcp_connections"
45
+ branch_labels = None
46
+ depends_on = None
47
+
48
+
49
+ def upgrade() -> None:
50
+ pass
51
+
52
+
53
+ def downgrade() -> None:
54
+ pass
55
+ {%- endif %}
@@ -301,7 +301,19 @@ def get_sync_engine() -> Engine:
301
301
  class AdminAuth(AuthenticationBackend):
302
302
  """Admin panel authentication backend.
303
303
 
304
- Requires superuser credentials to access the admin panel.
304
+ Requires app-admin credentials to access the admin panel. Note this is a
305
+ *local* password check — it does not consult any external IdP, so it must
306
+ never be the only thing standing between a self-registered account and the
307
+ database. Both conditions below matter:
308
+
309
+ - ``is_app_admin`` — the explicit admin flag, not just the ``role`` column,
310
+ so a row that acquired ``role=admin`` some other way is not enough.
311
+ {%- if cookiecutter.use_delegated_auth %}
312
+ - ``external_user_id`` — the row must correspond to a real IdP identity.
313
+ Under delegated auth the local ``/auth/register`` endpoint is not
314
+ generated, so no unlinked row should exist; this check makes that
315
+ guarantee local instead of relying on a route elsewhere staying absent.
316
+ {%- endif %}
305
317
  """
306
318
 
307
319
  async def login(self, request: Request) -> bool:
@@ -321,9 +333,14 @@ class AdminAuth(AuthenticationBackend):
321
333
 
322
334
  if (
323
335
  user
336
+ and user.is_active
324
337
  and user.hashed_password
325
338
  and verify_password(password, user.hashed_password)
326
339
  and user.has_role(UserRole.ADMIN)
340
+ and getattr(user, "is_app_admin", False)
341
+ {%- if cookiecutter.use_delegated_auth %}
342
+ and user.external_user_id is not None
343
+ {%- endif %}
327
344
  ):
328
345
  request.session["admin_user_id"] = str(user.id)
329
346
  request.session["admin_email"] = user.email
@@ -344,7 +361,12 @@ class AdminAuth(AuthenticationBackend):
344
361
 
345
362
  with DBSession(get_sync_engine()) as session:
346
363
  user = session.query(User).filter(User.id == admin_user_id).first()
347
- if user and user.has_role(UserRole.ADMIN) and user.is_active:
364
+ if (
365
+ user
366
+ and user.is_active
367
+ and user.has_role(UserRole.ADMIN)
368
+ and getattr(user, "is_app_admin", False)
369
+ ):
348
370
  return True
349
371
 
350
372
  request.session.clear()
@@ -512,6 +512,11 @@ async def get_current_user_ws(
512
512
  Tokens in query strings are NOT accepted — they leak into logs and
513
513
  Referer headers.
514
514
 
515
+ The token is validated by the same authority as ``get_current_user``: the
516
+ IdP's keys under delegated auth, this backend's ``SECRET_KEY`` otherwise.
517
+ The two must not diverge — a WebSocket that trusts a different issuer than
518
+ the REST API is an authentication bypass on whichever side is weaker.
519
+
515
520
  Raises:
516
521
  WebSocketException: If token is invalid or user not found. Raising the
517
522
  WebSocket-native exception lets Starlette close the handshake cleanly
@@ -527,6 +532,35 @@ async def get_current_user_ws(
527
532
  if not auth_token:
528
533
  raise WebSocketException(code=4001, reason="Missing authentication token")
529
534
 
535
+ {%- if cookiecutter.use_delegated_auth %}
536
+ payload = verify_idp_token(auth_token)
537
+ if payload is None:
538
+ raise WebSocketException(code=4001, reason="Invalid or expired token")
539
+
540
+ # No `type` check here: an external IdP does not mint our access/refresh
541
+ # distinction. The audience and issuer checks in verify_idp_token are what
542
+ # constrain which tokens this API accepts.
543
+ external_id = payload.get(settings.IDP_USER_ID_CLAIM)
544
+ if not external_id:
545
+ raise WebSocketException(code=4001, reason="Invalid token payload")
546
+
547
+ async with get_db_context() as db:
548
+ user_service = UserService(db)
549
+ user = await user_service.get_or_create_from_idp(
550
+ external_user_id=str(external_id),
551
+ email=payload.get(settings.IDP_EMAIL_CLAIM) or f"{external_id}@idp.local",
552
+ full_name=payload.get(settings.IDP_NAME_CLAIM),
553
+ )
554
+
555
+ if not user.is_active:
556
+ raise WebSocketException(code=4001, reason="User account is disabled")
557
+
558
+ # Eagerly load all columns, then detach from session to avoid
559
+ # "instance not bound to a Session" errors after the context manager exits
560
+ await db.refresh(user)
561
+ db.expunge(user)
562
+ return user
563
+ {%- else %}
530
564
  payload = verify_token(auth_token)
531
565
  if payload is None:
532
566
  raise WebSocketException(code=4001, reason="Invalid or expired token")
@@ -553,6 +587,7 @@ async def get_current_user_ws(
553
587
  await db.refresh(user)
554
588
  db.expunge(user)
555
589
  return user
590
+ {%- endif %}
556
591
 
557
592
 
558
593
  CurrentUserWS = Annotated[User, Depends(get_current_user_ws)]
@@ -639,18 +674,34 @@ def get_vectorstore(request: Request, embedder: EmbeddingSvc) -> BaseVectorStore
639
674
 
640
675
  VectorStoreSvc = Annotated[BaseVectorStore, Depends(get_vectorstore)]
641
676
 
642
- def get_retrieval_service(vector_store: VectorStoreSvc) -> RetrievalService:
677
+ {%- if cookiecutter.enable_reranker %}
678
+ def get_rerank_service(request: Request) -> RerankService:
679
+ """Get the reranker warmed at startup, or build one if warmup failed.
680
+
681
+ Reusing the lifespan instance is not an optimisation — a cross-encoder
682
+ reranker holds its model on the instance and loads it lazily, so a
683
+ per-request service reloads the model inside the request.
684
+ """
685
+ if hasattr(request.state, "rerank_service"):
686
+ return request.state.rerank_service # type: ignore[no-any-return]
687
+ return RerankService(settings=settings.rag)
688
+
689
+ RerankSvc = Annotated[RerankService, Depends(get_rerank_service)]
690
+
691
+ def get_retrieval_service(
692
+ vector_store: VectorStoreSvc, rerank_service: RerankSvc
693
+ ) -> RetrievalService:
643
694
  """Create RetrievalService instance."""
644
- {%- if cookiecutter.enable_reranker %}
645
- rerank_service = RerankService(settings=settings.rag)
646
695
  return RetrievalService(
647
696
  vector_store=vector_store,
648
697
  settings=settings.rag,
649
698
  rerank_service=rerank_service,
650
699
  )
651
- {%- else %}
700
+ {%- else %}
701
+ def get_retrieval_service(vector_store: VectorStoreSvc) -> RetrievalService:
702
+ """Create RetrievalService instance."""
652
703
  return RetrievalService(vector_store=vector_store, settings=settings.rag)
653
- {%- endif %}
704
+ {%- endif %}
654
705
 
655
706
  RetrievalSvc = Annotated[RetrievalService, Depends(get_retrieval_service)]
656
707