tempest-fastapi-sdk 0.46.0__tar.gz → 0.47.0__tar.gz

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 (391) hide show
  1. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/CHANGELOG.md +23 -0
  2. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/PKG-INFO +16 -1
  3. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/README.md +15 -0
  4. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/cli.en.md +42 -0
  5. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/cli.md +42 -0
  6. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/index.en.md +1 -1
  7. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/index.md +1 -1
  8. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/pyproject.toml +1 -1
  9. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/__init__.py +1 -1
  10. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/db.py +113 -0
  11. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/main.py +2 -0
  12. tempest_fastapi_sdk-0.47.0/tempest_fastapi_sdk/cli/secrets.py +159 -0
  13. tempest_fastapi_sdk-0.47.0/tests/cli/test_db_seed.py +107 -0
  14. tempest_fastapi_sdk-0.47.0/tests/cli/test_secrets.py +78 -0
  15. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/uv.lock +1 -1
  16. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/.github/workflows/ci.yml +0 -0
  17. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/.github/workflows/docs.yml +0 -0
  18. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/.github/workflows/release-pypi.yml +0 -0
  19. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/.gitignore +0 -0
  20. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/.python-version +0 -0
  21. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/CLAUDE.md +0 -0
  22. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/Makefile +0 -0
  23. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/architecture.en.md +0 -0
  24. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/architecture.md +0 -0
  25. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/changelog.en.md +0 -0
  26. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/changelog.md +0 -0
  27. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/contributing.en.md +0 -0
  28. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/contributing.md +0 -0
  29. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/index.en.md +0 -0
  30. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/index.md +0 -0
  31. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/installation.en.md +0 -0
  32. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/installation.md +0 -0
  33. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/index.en.md +0 -0
  34. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/index.md +0 -0
  35. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/api.en.md +0 -0
  36. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/api.md +0 -0
  37. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/business-rules.en.md +0 -0
  38. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/business-rules.md +0 -0
  39. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/domain.en.md +0 -0
  40. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/domain.md +0 -0
  41. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/flows.en.md +0 -0
  42. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/flows.md +0 -0
  43. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/index.en.md +0 -0
  44. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/learning/marketplace/index.md +0 -0
  45. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/migration.en.md +0 -0
  46. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/migration.md +0 -0
  47. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/admin.en.md +0 -0
  48. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/admin.md +0 -0
  49. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/auth-flow.en.md +0 -0
  50. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/auth-flow.md +0 -0
  51. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/br-helpers.en.md +0 -0
  52. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/br-helpers.md +0 -0
  53. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/cache.en.md +0 -0
  54. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/cache.md +0 -0
  55. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/database.en.md +0 -0
  56. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/database.md +0 -0
  57. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/deploy-safety.en.md +0 -0
  58. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/deploy-safety.md +0 -0
  59. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/downloads.en.md +0 -0
  60. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/downloads.md +0 -0
  61. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/email.en.md +0 -0
  62. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/email.md +0 -0
  63. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/http-client.en.md +0 -0
  64. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/http-client.md +0 -0
  65. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/http.en.md +0 -0
  66. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/http.md +0 -0
  67. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/idempotency.en.md +0 -0
  68. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/idempotency.md +0 -0
  69. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/logging.en.md +0 -0
  70. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/logging.md +0 -0
  71. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/metrics.en.md +0 -0
  72. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/metrics.md +0 -0
  73. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/mfa.en.md +0 -0
  74. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/mfa.md +0 -0
  75. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/multi-tenant.en.md +0 -0
  76. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/multi-tenant.md +0 -0
  77. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/observability.en.md +0 -0
  78. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/observability.md +0 -0
  79. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/outbox.en.md +0 -0
  80. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/outbox.md +0 -0
  81. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/queue-tasks.en.md +0 -0
  82. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/queue-tasks.md +0 -0
  83. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/realtime.en.md +0 -0
  84. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/realtime.md +0 -0
  85. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/security.en.md +0 -0
  86. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/security.md +0 -0
  87. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/sessions.en.md +0 -0
  88. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/sessions.md +0 -0
  89. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/storage.en.md +0 -0
  90. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/storage.md +0 -0
  91. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/testing.en.md +0 -0
  92. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/testing.md +0 -0
  93. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/uploads.en.md +0 -0
  94. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/uploads.md +0 -0
  95. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/utilities.en.md +0 -0
  96. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/utilities.md +0 -0
  97. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/webpush.en.md +0 -0
  98. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/webpush.md +0 -0
  99. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/websocket.en.md +0 -0
  100. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/recipes/websocket.md +0 -0
  101. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/reference.en.md +0 -0
  102. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/reference.md +0 -0
  103. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/roadmap.en.md +0 -0
  104. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/roadmap.md +0 -0
  105. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/tutorial.en.md +0 -0
  106. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/docs/tutorial.md +0 -0
  107. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/mkdocs.yml +0 -0
  108. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/scripts/extract_recipe.py +0 -0
  109. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/__init__.py +0 -0
  110. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/auth.py +0 -0
  111. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/config.py +0 -0
  112. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/forms.py +0 -0
  113. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/router.py +0 -0
  114. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/session.py +0 -0
  115. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/site.py +0 -0
  116. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/static/admin.css +0 -0
  117. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/base.html +0 -0
  118. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -0
  119. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/detail.html +0 -0
  120. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/form.html +0 -0
  121. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/list.html +0 -0
  122. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
  123. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/logs.html +0 -0
  124. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/admin/templates/mfa.html +0 -0
  125. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
  126. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
  127. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
  128. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/dependencies/auth.py +0 -0
  129. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
  130. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
  131. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
  132. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
  133. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
  134. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/graceful.py +0 -0
  135. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
  136. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
  137. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
  138. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
  139. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
  140. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
  141. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
  142. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
  143. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
  144. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/server.py +0 -0
  145. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/static.py +0 -0
  146. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/tracing.py +0 -0
  147. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
  148. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
  149. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
  150. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/router.py +0 -0
  151. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
  152. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/service.py +0 -0
  153. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/templates/activation.html +0 -0
  154. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/templates/activation_error.html +0 -0
  155. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/templates/activation_success.html +0 -0
  156. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/templates/password_reset.html +0 -0
  157. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/templates/password_reset_error.html +0 -0
  158. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/templates/password_reset_form.html +0 -0
  159. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/auth/templates/password_reset_success.html +0 -0
  160. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
  161. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
  162. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
  163. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
  164. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
  165. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +0 -0
  166. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
  167. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
  168. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
  169. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
  170. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
  171. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +0 -0
  172. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/__init__.py.tmpl +0 -0
  173. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/auth.py.tmpl +0 -0
  174. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/resources.py.tmpl +0 -0
  175. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/api/routers/__init__.py.tmpl +0 -0
  176. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
  177. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
  178. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
  179. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -0
  180. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
  181. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/__init__.py.tmpl +0 -0
  182. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/user.py.tmpl +0 -0
  183. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/db/repositories/__init__.py.tmpl +0 -0
  184. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
  185. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
  186. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
  187. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
  188. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
  189. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
  190. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
  191. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
  192. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
  193. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/new.py +0 -0
  194. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/src_layers.py +0 -0
  195. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/cli/user.py +0 -0
  196. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
  197. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
  198. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
  199. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/core/context.py +0 -0
  200. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/core/enums.py +0 -0
  201. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/core/logging.py +0 -0
  202. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/__init__.py +0 -0
  203. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
  204. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
  205. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
  206. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/connection.py +0 -0
  207. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
  208. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
  209. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/model.py +0 -0
  210. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/outbox.py +0 -0
  211. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/repository.py +0 -0
  212. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/slow_query.py +0 -0
  213. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/tenant.py +0 -0
  214. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
  215. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
  216. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
  217. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
  218. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
  219. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
  220. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
  221. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
  222. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
  223. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
  224. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
  225. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
  226. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
  227. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/py.typed +0 -0
  228. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
  229. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
  230. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
  231. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
  232. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
  233. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
  234. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/schemas/pagination.py +0 -0
  235. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
  236. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
  237. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/services/base.py +0 -0
  238. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
  239. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
  240. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
  241. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
  242. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
  243. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
  244. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
  245. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
  246. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/settings/base.py +0 -0
  247. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
  248. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sse/__init__.py +0 -0
  249. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
  250. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
  251. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
  252. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
  253. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
  254. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
  255. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
  256. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/testing/database.py +0 -0
  257. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
  258. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
  259. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
  260. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
  261. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/download.py +0 -0
  262. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/email.py +0 -0
  263. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
  264. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
  265. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/log.py +0 -0
  266. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
  267. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
  268. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/password.py +0 -0
  269. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
  270. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
  271. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
  272. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
  273. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
  274. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
  275. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
  276. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
  277. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
  278. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
  279. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
  280. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
  281. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/__init__.py +0 -0
  282. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/__init__.py +0 -0
  283. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/test_auth.py +0 -0
  284. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/test_forms.py +0 -0
  285. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/test_logs_nav.py +0 -0
  286. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/test_mfa.py +0 -0
  287. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/test_router.py +0 -0
  288. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/test_site.py +0 -0
  289. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/admin/test_user_model.py +0 -0
  290. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/__init__.py +0 -0
  291. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_body_size.py +0 -0
  292. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_cookies.py +0 -0
  293. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_cors.py +0 -0
  294. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_csrf.py +0 -0
  295. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_dependencies_auth.py +0 -0
  296. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_graceful.py +0 -0
  297. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_handlers.py +0 -0
  298. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_health_router.py +0 -0
  299. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_idempotency.py +0 -0
  300. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_jwt_dependency.py +0 -0
  301. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_logs_router.py +0 -0
  302. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_oauth.py +0 -0
  303. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_prometheus.py +0 -0
  304. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_rate_limit.py +0 -0
  305. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_request_id_middleware.py +0 -0
  306. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_role_dependency.py +0 -0
  307. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_server.py +0 -0
  308. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_static.py +0 -0
  309. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_tool_spec.py +0 -0
  310. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_tracing.py +0 -0
  311. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_webhooks.py +0 -0
  312. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/api/test_webhooks_rsa.py +0 -0
  313. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/auth/__init__.py +0 -0
  314. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/auth/test_mfa.py +0 -0
  315. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/auth/test_service.py +0 -0
  316. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cache/__init__.py +0 -0
  317. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cache/test_decorator.py +0 -0
  318. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cache/test_redis_manager.py +0 -0
  319. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cli/__init__.py +0 -0
  320. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cli/test_db.py +0 -0
  321. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cli/test_docker_compose.py +0 -0
  322. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cli/test_generate.py +0 -0
  323. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cli/test_main.py +0 -0
  324. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/cli/test_user.py +0 -0
  325. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/conftest.py +0 -0
  326. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/controllers/__init__.py +0 -0
  327. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/controllers/test_base.py +0 -0
  328. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/core/__init__.py +0 -0
  329. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/core/test_context.py +0 -0
  330. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/core/test_enums.py +0 -0
  331. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/core/test_logging.py +0 -0
  332. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/__init__.py +0 -0
  333. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_alembic_hooks.py +0 -0
  334. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_bulk_ops.py +0 -0
  335. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_connection.py +0 -0
  336. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_migrations.py +0 -0
  337. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_mixins.py +0 -0
  338. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_model.py +0 -0
  339. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_outbox.py +0 -0
  340. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_repository.py +0 -0
  341. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_safe_upgrade.py +0 -0
  342. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_slow_query.py +0 -0
  343. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/db/test_tenant.py +0 -0
  344. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/exceptions/__init__.py +0 -0
  345. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/exceptions/test_exceptions.py +0 -0
  346. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/queue/__init__.py +0 -0
  347. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/queue/test_manager.py +0 -0
  348. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/schemas/__init__.py +0 -0
  349. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/schemas/test_base.py +0 -0
  350. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/schemas/test_cursor_pagination.py +0 -0
  351. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/schemas/test_link_headers.py +0 -0
  352. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/schemas/test_pagination.py +0 -0
  353. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/schemas/test_response.py +0 -0
  354. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/services/__init__.py +0 -0
  355. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/services/test_base.py +0 -0
  356. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/sessions/__init__.py +0 -0
  357. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/sessions/test_sessions.py +0 -0
  358. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/settings/__init__.py +0 -0
  359. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/settings/test_base.py +0 -0
  360. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/settings/test_mixins.py +0 -0
  361. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/sse/__init__.py +0 -0
  362. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/sse/test_event_stream.py +0 -0
  363. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/storage/__init__.py +0 -0
  364. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/storage/test_minio_client.py +0 -0
  365. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/tasks/__init__.py +0 -0
  366. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/tasks/test_manager.py +0 -0
  367. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/tasks/test_scheduler.py +0 -0
  368. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/testing/__init__.py +0 -0
  369. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/testing/test_database.py +0 -0
  370. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/__init__.py +0 -0
  371. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_client_ip.py +0 -0
  372. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_datetime.py +0 -0
  373. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_dict.py +0 -0
  374. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_download.py +0 -0
  375. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_email.py +0 -0
  376. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_http_client.py +0 -0
  377. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_jwt.py +0 -0
  378. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_lazy_extras.py +0 -0
  379. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_log.py +0 -0
  380. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_metrics.py +0 -0
  381. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_opaque_token.py +0 -0
  382. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_password.py +0 -0
  383. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_regex.py +0 -0
  384. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_storage_backends.py +0 -0
  385. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_throttle.py +0 -0
  386. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/utils/test_upload.py +0 -0
  387. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/webpush/__init__.py +0 -0
  388. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/webpush/test_dispatcher.py +0 -0
  389. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/webpush/test_schemas.py +0 -0
  390. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/websockets/__init__.py +0 -0
  391. {tempest_fastapi_sdk-0.46.0 → tempest_fastapi_sdk-0.47.0}/tests/websockets/test_hub_and_router.py +0 -0
@@ -5,6 +5,29 @@ All notable changes to **tempest-fastapi-sdk** are listed below.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.47.0] — 2026-06-11
9
+
10
+ ### Added
11
+
12
+ - **`tempest db seed`** — runs a project seed callable
13
+ (default `src.db.seeds:seed`, dotted `module:callable`, sync or async,
14
+ taking one `AsyncSession`) inside a managed session: commit on
15
+ success, rollback on error. The SDK only wires the session lifecycle;
16
+ the callable owns what gets inserted. Prints the row count when the
17
+ callable returns an `int`.
18
+ - **`tempest secrets rotate`** — generates fresh URL-safe secrets for
19
+ the keys a service signs/authenticates with (`JWT_SECRET` /
20
+ `TOKEN_SECRET` by default; override with `--keys`) and rewrites the
21
+ matching `.env` lines **in place** (existing keys replaced, missing
22
+ keys appended) after a `.env.bak` backup. `--print` writes nothing and
23
+ emits the values to stdout; `--length` sets the entropy; `--no-backup`
24
+ skips the backup.
25
+
26
+ ### Docs
27
+
28
+ - CLI recipe (bilingual) gains **`db seed`** and **`secrets rotate`**
29
+ sections; README CLI section and recipes index updated.
30
+
8
31
  ## [0.46.0] — 2026-06-11
9
32
 
10
33
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tempest-fastapi-sdk
3
- Version: 0.46.0
3
+ Version: 0.47.0
4
4
  Summary: Shared FastAPI building blocks: base schemas, ORM model, async repository, exceptions, pagination and settings — the conventions used across Tempest projects.
5
5
  Project-URL: Homepage, https://github.com/mauriciobenjamin700/tempest-fastapi-sdk
6
6
  Project-URL: Repository, https://github.com/mauriciobenjamin700/tempest-fastapi-sdk
@@ -3411,6 +3411,21 @@ tempest db upgrade <rev> # upgrade to a specific revisio
3411
3411
  tempest db downgrade # roll back one step
3412
3412
  tempest db current # print the applied revision
3413
3413
  tempest db history -v # revisions newest → oldest, verbose
3414
+ tempest db seed # run src.db.seeds:seed in one session
3415
+ tempest db seed --seed src.db.fixtures:demo # custom seed callable
3416
+ ```
3417
+
3418
+ `tempest db seed` runs a project seed callable (default `src.db.seeds:seed`, sync or async, taking one `AsyncSession`) inside a managed session — commit on success, rollback on error.
3419
+
3420
+ #### Secrets — `tempest secrets`
3421
+
3422
+ Generates and rotates application secrets (`JWT_SECRET` / `TOKEN_SECRET` by default), rewriting the matching `.env` lines in place after a `.env.bak` backup; `--print` writes nothing and emits the values to stdout.
3423
+
3424
+ ```bash
3425
+ tempest secrets rotate # rotate JWT_SECRET + TOKEN_SECRET in .env
3426
+ tempest secrets rotate --print # just print, write nothing
3427
+ tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
3428
+ tempest secrets rotate --length 64 --no-backup
3414
3429
  ```
3415
3430
 
3416
3431
  #### Users — `tempest user`
@@ -3309,6 +3309,21 @@ tempest db upgrade <rev> # upgrade to a specific revisio
3309
3309
  tempest db downgrade # roll back one step
3310
3310
  tempest db current # print the applied revision
3311
3311
  tempest db history -v # revisions newest → oldest, verbose
3312
+ tempest db seed # run src.db.seeds:seed in one session
3313
+ tempest db seed --seed src.db.fixtures:demo # custom seed callable
3314
+ ```
3315
+
3316
+ `tempest db seed` runs a project seed callable (default `src.db.seeds:seed`, sync or async, taking one `AsyncSession`) inside a managed session — commit on success, rollback on error.
3317
+
3318
+ #### Secrets — `tempest secrets`
3319
+
3320
+ Generates and rotates application secrets (`JWT_SECRET` / `TOKEN_SECRET` by default), rewriting the matching `.env` lines in place after a `.env.bak` backup; `--print` writes nothing and emits the values to stdout.
3321
+
3322
+ ```bash
3323
+ tempest secrets rotate # rotate JWT_SECRET + TOKEN_SECRET in .env
3324
+ tempest secrets rotate --print # just print, write nothing
3325
+ tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
3326
+ tempest secrets rotate --length 64 --no-backup
3312
3327
  ```
3313
3328
 
3314
3329
  #### Users — `tempest user`
@@ -140,8 +140,29 @@ tempest db downgrade <rev> # roll back to a specific revis
140
140
  tempest db current # print the applied revision
141
141
  tempest db history # revisions newest → oldest
142
142
  tempest db history -v # with full message body
143
+ tempest db seed # runs src.db.seeds:seed
144
+ tempest db seed --seed src.db.fixtures:demo # custom callable
143
145
  ```
144
146
 
147
+ #### Seed the database — `tempest db seed`
148
+
149
+ Runs a project seed callable inside a managed session (commit on success, rollback on error). The callable takes a positional `AsyncSession` and may be sync or async; what it inserts is up to you — the SDK only wires the session lifecycle. Defaults to importing `src.db.seeds:seed`.
150
+
151
+ ```python
152
+ from sqlalchemy.ext.asyncio import AsyncSession
153
+
154
+ from src.db.models import CategoryModel
155
+
156
+
157
+ async def seed(session: AsyncSession) -> int:
158
+ """Seed initial categories. Return the count (optional)."""
159
+ session.add_all([CategoryModel(name="Books"), CategoryModel(name="Games")])
160
+ await session.flush()
161
+ return 2
162
+ ```
163
+
164
+ When the callable returns an `int`, the CLI prints the count: `Seeded via src.db.seeds:seed (2 rows).`
165
+
145
166
  ### Users — `tempest user`
146
167
 
147
168
  Seed and list users using the project's concrete ``UserModel`` (default ``src.db.models:UserModel``). Bootstraps the first admin without manual SQL.
@@ -180,6 +201,27 @@ tempest user list --admin # admins only
180
201
 
181
202
  ``DATABASE_URL`` resolves the same way as ``tempest db`` (env var > settings > alembic.ini).
182
203
 
204
+ ### Secrets — `tempest secrets`
205
+
206
+ Generate and rotate application secrets (`JWT_SECRET` / `TOKEN_SECRET` by default), rewriting the matching `.env` lines **in place** — backing up the old file first — and leaving every other line untouched.
207
+
208
+ ```bash
209
+ # Rotate JWT_SECRET and TOKEN_SECRET in .env (writes .env.bak)
210
+ tempest secrets rotate
211
+
212
+ # Just print the new values (writes nothing) — pipe into a secret manager
213
+ tempest secrets rotate --print
214
+
215
+ # Custom keys and file
216
+ tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
217
+
218
+ # More entropy, no backup
219
+ tempest secrets rotate --length 64 --no-backup
220
+ ```
221
+
222
+ !!! warning
223
+ Rotating `JWT_SECRET` invalidates every token signed with the old value: users are logged out and pending reset/activation links stop working. Rotate during a maintenance window and restart the service to load the new values.
224
+
183
225
  ### Regenerating `docker-compose.yaml` in an existing project
184
226
 
185
227
  When you change installed extras (`uv add "tempest-fastapi-sdk[minio]"`) or the SDK bumps image versions, regenerate with:
@@ -188,8 +188,29 @@ tempest db downgrade <rev> # rollback até rev específico
188
188
  tempest db current # imprime revision aplicado
189
189
  tempest db history # histórico de revisions
190
190
  tempest db history -v # com message body completo
191
+ tempest db seed # roda src.db.seeds:seed
192
+ tempest db seed --seed src.db.fixtures:demo # callable customizado
191
193
  ```
192
194
 
195
+ #### Popular o banco — `tempest db seed`
196
+
197
+ Roda um callable de seed do projeto dentro de uma sessão gerenciada (commit no sucesso, rollback no erro). O callable recebe uma `AsyncSession` posicional e pode ser sync ou async; o que ele insere é decisão sua — o SDK só cuida do ciclo de vida da sessão. Por padrão importa `src.db.seeds:seed`.
198
+
199
+ ```python
200
+ from sqlalchemy.ext.asyncio import AsyncSession
201
+
202
+ from src.db.models import CategoryModel
203
+
204
+
205
+ async def seed(session: AsyncSession) -> int:
206
+ """Popula categorias iniciais. Retorna a contagem (opcional)."""
207
+ session.add_all([CategoryModel(name="Livros"), CategoryModel(name="Games")])
208
+ await session.flush()
209
+ return 2
210
+ ```
211
+
212
+ Quando o callable devolve um `int`, a CLI mostra a contagem: `Seeded via src.db.seeds:seed (2 rows).`
213
+
193
214
  ### Usuários — `tempest user`
194
215
 
195
216
  Insere/lista usuários direto no banco usando o `UserModel` concreto do projeto (default `src.db.models:UserModel`). Útil pra bootstrapear o primeiro admin sem rodar SQL manual.
@@ -228,6 +249,27 @@ tempest user list --admin # só admins
228
249
 
229
250
  Resolução do `DATABASE_URL` igual ao `tempest db` (env var > settings > alembic.ini).
230
251
 
252
+ ### Segredos — `tempest secrets`
253
+
254
+ Gera e rotaciona os segredos da aplicação (`JWT_SECRET` / `TOKEN_SECRET` por padrão), reescrevendo as linhas correspondentes no `.env` **no lugar** — fazendo backup do arquivo antigo antes — e deixando as outras linhas intactas.
255
+
256
+ ```bash
257
+ # Rotaciona JWT_SECRET e TOKEN_SECRET no .env (gera .env.bak)
258
+ tempest secrets rotate
259
+
260
+ # Só imprime os novos valores (não escreve nada) — pra pipar num secret manager
261
+ tempest secrets rotate --print
262
+
263
+ # Chaves e arquivo customizados
264
+ tempest secrets rotate --keys JWT_SECRET,SESSION_SECRET --env .env.prod
265
+
266
+ # Mais entropia, sem backup
267
+ tempest secrets rotate --length 64 --no-backup
268
+ ```
269
+
270
+ !!! warning
271
+ Rotacionar `JWT_SECRET` invalida todo token assinado com o valor antigo: usuários são deslogados e links de reset/ativação pendentes param de funcionar. Rotacione numa janela de manutenção e reinicie o serviço pra carregar os novos valores.
272
+
231
273
  #### Gates de qualidade
232
274
 
233
275
  Os comandos de lint chamam a ferramenta do projeto. Eles procuram o executável no `PATH` primeiro e, caso contrário, caem para `uv run <tool>` para que um virtualenv local do projeto funcione sem ativação manual.
@@ -27,7 +27,7 @@ Bite-sized "I want to wire X" walkthroughs. Each page starts with **what problem
27
27
  | **[Utilities »](utilities.md)** | `utcnow`/`to_utc`, `modify_dict`, `get_client_ip`, opaque tokens (`generate_opaque_token`) |
28
28
  | **[Testing »](testing.md)** | `test_session`, `test_database`, in-memory SQLite, pytest fixtures |
29
29
  | **[Safe deploys »](deploy-safety.md)** | `AlembicHelper.safe_upgrade` (blocks DROPs), `GracefulShutdownMiddleware` |
30
- | **[CLI »](cli.md)** | `tempest new` / `lint` / `fix` / `format` / `type` / `test` / `check` |
30
+ | **[CLI »](cli.md)** | `tempest new` / `db` (+ `seed`) / `user` / `secrets rotate` / `lint` / `fix` / `format` / `type` / `test` / `check` |
31
31
  | **[Security »](security.md)** | `AttemptThrottle`, opaque-token helpers, `HardenedStaticFiles`, security headers |
32
32
  | **[Brazilian helpers »](br-helpers.md)** | CPF / CNPJ / CEP / phone validation + normalization |
33
33
 
@@ -27,7 +27,7 @@ Passo a passo curtos no estilo "quero conectar X". Cada página começa com **qu
27
27
  | **[Utilitários »](utilities.md)** | `utcnow`/`to_utc`, `modify_dict`, `get_client_ip`, tokens opacos (`generate_opaque_token`) |
28
28
  | **[Testes »](testing.md)** | `test_session`, `test_database`, SQLite em memória, fixtures pytest |
29
29
  | **[Deploy seguro »](deploy-safety.md)** | `AlembicHelper.safe_upgrade` (barra DROPs), `GracefulShutdownMiddleware` |
30
- | **[CLI »](cli.md)** | `tempest new` / `lint` / `fix` / `format` / `type` / `test` / `check` |
30
+ | **[CLI »](cli.md)** | `tempest new` / `db` (+ `seed`) / `user` / `secrets rotate` / `lint` / `fix` / `format` / `type` / `test` / `check` |
31
31
  | **[Segurança »](security.md)** | `AttemptThrottle`, helpers de token opaco, `HardenedStaticFiles`, headers de segurança |
32
32
  | **[Helpers brasileiros »](br-helpers.md)** | validação + normalização de CPF / CNPJ / CEP / telefone |
33
33
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tempest-fastapi-sdk"
3
- version = "0.46.0"
3
+ version = "0.47.0"
4
4
  description = "Shared FastAPI building blocks: base schemas, ORM model, async repository, exceptions, pagination and settings — the conventions used across Tempest projects."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -250,7 +250,7 @@ from tempest_fastapi_sdk.websockets import (
250
250
  make_websocket_router,
251
251
  )
252
252
 
253
- __version__: str = "0.46.0"
253
+ __version__: str = "0.47.0"
254
254
 
255
255
  __all__: list[str] = [
256
256
  "BASE_COLUMN_ORDER",
@@ -20,12 +20,21 @@ stripped automatically before Alembic runs.
20
20
 
21
21
  from __future__ import annotations
22
22
 
23
+ import asyncio
24
+ import importlib
25
+ import inspect
23
26
  import os
24
27
  import sys
25
28
  from pathlib import Path
29
+ from typing import TYPE_CHECKING, Any, cast
26
30
 
27
31
  import typer
28
32
 
33
+ if TYPE_CHECKING:
34
+ from collections.abc import Callable
35
+
36
+ from sqlalchemy.ext.asyncio import AsyncSession
37
+
29
38
 
30
39
  def _resolve_database_url(explicit: str | None) -> str | None:
31
40
  """Pick the DB URL using the documented priority order.
@@ -95,6 +104,70 @@ def _helper(
95
104
  return AlembicHelper(str(ini), db_url=database_url)
96
105
 
97
106
 
107
+ def _load_seed_callable(spec: str) -> Callable[[AsyncSession], Any]:
108
+ """Import a ``module.path:callable`` seed entry point.
109
+
110
+ Args:
111
+ spec (str): Dotted spec ``"module.path:callable"``.
112
+
113
+ Returns:
114
+ Callable[[AsyncSession], Any]: The seed callable (sync or async).
115
+
116
+ Raises:
117
+ typer.Exit: When the spec is malformed, the import fails, or the
118
+ attribute is not callable.
119
+ """
120
+ module_path, _, attr = spec.partition(":")
121
+ if not module_path or not attr:
122
+ typer.echo(
123
+ f"error: --seed must be 'module.path:callable', got {spec!r}",
124
+ err=True,
125
+ )
126
+ raise typer.Exit(2)
127
+ cwd = str(Path.cwd())
128
+ if cwd not in sys.path:
129
+ sys.path.insert(0, cwd)
130
+ try:
131
+ module = importlib.import_module(module_path)
132
+ except ImportError as exc:
133
+ typer.echo(f"error: cannot import {module_path!r}: {exc}", err=True)
134
+ raise typer.Exit(2) from exc
135
+ seed: object = getattr(module, attr, None)
136
+ if not callable(seed):
137
+ typer.echo(
138
+ f"error: {spec} is not callable (got {type(seed).__name__}).",
139
+ err=True,
140
+ )
141
+ raise typer.Exit(2)
142
+ return cast("Callable[[AsyncSession], Any]", seed)
143
+
144
+
145
+ async def _run_seed(database_url: str, seed: Callable[[AsyncSession], Any]) -> Any:
146
+ """Open one managed session and run the seed callable in it.
147
+
148
+ Args:
149
+ database_url (str): The resolved database URL.
150
+ seed (Callable[[AsyncSession], Any]): The seed callable; awaited
151
+ when it returns a coroutine.
152
+
153
+ Returns:
154
+ Any: Whatever the seed callable returns (e.g. a row count), or
155
+ ``None``.
156
+ """
157
+ from tempest_fastapi_sdk import AsyncDatabaseManager
158
+
159
+ manager = AsyncDatabaseManager(database_url)
160
+ await manager.connect()
161
+ try:
162
+ async with manager.get_session_context() as session:
163
+ result = seed(session)
164
+ if inspect.isawaitable(result):
165
+ result = await result
166
+ return result
167
+ finally:
168
+ await manager.disconnect()
169
+
170
+
98
171
  db_app: typer.Typer = typer.Typer(
99
172
  name="db",
100
173
  help="Alembic migration helpers (create / apply / inspect).",
@@ -242,6 +315,46 @@ def db_current(
242
315
  typer.echo(current or "(no revision applied)")
243
316
 
244
317
 
318
+ @db_app.command("seed")
319
+ def db_seed(
320
+ seed_spec: str = typer.Option(
321
+ "src.db.seeds:seed",
322
+ "--seed",
323
+ "-s",
324
+ help=(
325
+ "Dotted 'module.path:callable' to run. The callable receives "
326
+ "one positional AsyncSession and may be sync or async. Defaults "
327
+ "to the scaffolded 'src.db.seeds:seed'."
328
+ ),
329
+ ),
330
+ database_url: str | None = typer.Option(
331
+ None,
332
+ "--database-url",
333
+ help="Override DATABASE_URL for this run.",
334
+ ),
335
+ ) -> None:
336
+ """Run a project seed callable inside one managed session.
337
+
338
+ Builds an :class:`AsyncDatabaseManager` from the resolved URL, opens
339
+ a session (committed on success, rolled back on error), and invokes
340
+ the dotted callable with it. The callable owns what gets inserted —
341
+ the SDK only wires the session lifecycle so seeding looks the same
342
+ across every service.
343
+ """
344
+ url = _resolve_database_url(database_url)
345
+ if url is None:
346
+ typer.echo(
347
+ "error: no database URL. Pass --database-url, set DATABASE_URL, "
348
+ "or run inside a project with src/core/settings.py.",
349
+ err=True,
350
+ )
351
+ raise typer.Exit(2)
352
+ seed_callable = _load_seed_callable(seed_spec)
353
+ result = asyncio.run(_run_seed(url, seed_callable))
354
+ suffix = f" ({result} rows)" if isinstance(result, int) else ""
355
+ typer.echo(f"Seeded via {seed_spec}{suffix}.")
356
+
357
+
245
358
  @db_app.command("history")
246
359
  def db_history(
247
360
  verbose: bool = typer.Option(
@@ -14,6 +14,7 @@ from tempest_fastapi_sdk.cli import generate as generate_module
14
14
  from tempest_fastapi_sdk.cli import lint as lint_module
15
15
  from tempest_fastapi_sdk.cli import new as new_module
16
16
  from tempest_fastapi_sdk.cli.db import db_app
17
+ from tempest_fastapi_sdk.cli.secrets import secrets_app
17
18
  from tempest_fastapi_sdk.cli.user import user_app
18
19
 
19
20
  # Typer >= 0.13 vendors its own copy of Click under ``typer._click``, so a
@@ -136,6 +137,7 @@ app: typer.Typer = typer.Typer(
136
137
  )
137
138
  app.add_typer(db_app, name="db")
138
139
  app.add_typer(user_app, name="user")
140
+ app.add_typer(secrets_app, name="secrets")
139
141
 
140
142
 
141
143
  def _print_version(value: bool) -> None:
@@ -0,0 +1,159 @@
1
+ """``tempest secrets`` — generate and rotate application secrets.
2
+
3
+ Rotating a leaked (or simply stale) signing secret should be one
4
+ command, not a hunt through ``.env`` with a hand-rolled
5
+ ``openssl rand``. ``tempest secrets rotate`` generates fresh,
6
+ URL-safe random values for the secret keys a Tempest service uses
7
+ (``JWT_SECRET`` / ``TOKEN_SECRET`` by default), rewrites the matching
8
+ lines in ``.env`` in place — backing the old file up first — and
9
+ leaves every other line untouched.
10
+
11
+ !!! warning
12
+ Rotating ``JWT_SECRET`` invalidates every token signed with the
13
+ old one: users are logged out and pending password-reset /
14
+ activation links stop working. Rotate during a maintenance window
15
+ (or run two secrets in parallel if you need zero-downtime
16
+ rotation).
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import secrets
22
+ from pathlib import Path
23
+
24
+ import typer
25
+
26
+ # Secret env vars a Tempest service signs/authenticates with. MinIO and
27
+ # database credentials are intentionally excluded — those are external
28
+ # credentials, not values the service is free to regenerate.
29
+ _DEFAULT_KEYS: tuple[str, ...] = ("JWT_SECRET", "TOKEN_SECRET")
30
+
31
+ secrets_app: typer.Typer = typer.Typer(
32
+ name="secrets",
33
+ help="Generate and rotate application secrets.",
34
+ no_args_is_help=True,
35
+ )
36
+
37
+
38
+ def _generate(length: int) -> str:
39
+ """Return a URL-safe random secret with at least ``length`` bytes.
40
+
41
+ Args:
42
+ length (int): Number of random bytes of entropy.
43
+
44
+ Returns:
45
+ str: A URL-safe token (longer than ``length`` chars due to the
46
+ base64 encoding).
47
+ """
48
+ return secrets.token_urlsafe(length)
49
+
50
+
51
+ def _rewrite_env(path: Path, new_values: dict[str, str]) -> tuple[list[str], list[str]]:
52
+ """Apply ``new_values`` to a ``.env`` file's content.
53
+
54
+ Existing ``KEY=...`` lines are replaced in place (preserving order
55
+ and surrounding lines); keys not present are appended at the end.
56
+
57
+ Args:
58
+ path (Path): The ``.env`` file (may not exist yet).
59
+ new_values (dict[str, str]): ``KEY -> new secret`` mapping.
60
+
61
+ Returns:
62
+ tuple[list[str], list[str]]: ``(updated_keys, appended_keys)``.
63
+ """
64
+ lines = path.read_text(encoding="utf-8").splitlines() if path.is_file() else []
65
+ remaining = dict(new_values)
66
+ updated: list[str] = []
67
+ out: list[str] = []
68
+ for line in lines:
69
+ stripped = line.lstrip()
70
+ replaced = False
71
+ for key in list(remaining):
72
+ if stripped.startswith(f"{key}="):
73
+ out.append(f"{key}={remaining.pop(key)}")
74
+ updated.append(key)
75
+ replaced = True
76
+ break
77
+ if not replaced:
78
+ out.append(line)
79
+ appended = list(remaining)
80
+ for key in appended:
81
+ out.append(f"{key}={remaining[key]}")
82
+ path.write_text("\n".join(out) + "\n", encoding="utf-8")
83
+ return updated, appended
84
+
85
+
86
+ @secrets_app.command("rotate")
87
+ def secrets_rotate(
88
+ keys: str = typer.Option(
89
+ ",".join(_DEFAULT_KEYS),
90
+ "--keys",
91
+ "-k",
92
+ help=(
93
+ "Comma-separated env var names to rotate. Defaults to "
94
+ "'JWT_SECRET,TOKEN_SECRET'."
95
+ ),
96
+ ),
97
+ env_file: str = typer.Option(
98
+ ".env",
99
+ "--env",
100
+ "-e",
101
+ help="Path to the .env file to rewrite.",
102
+ ),
103
+ length: int = typer.Option(
104
+ 48,
105
+ "--length",
106
+ "-l",
107
+ min=16,
108
+ help="Bytes of entropy per secret (URL-safe encoded longer).",
109
+ ),
110
+ show: bool = typer.Option(
111
+ False,
112
+ "--print",
113
+ help="Print the generated secrets to stdout instead of writing .env.",
114
+ ),
115
+ no_backup: bool = typer.Option(
116
+ False,
117
+ "--no-backup",
118
+ help="Skip writing the .env.bak backup before rewriting.",
119
+ ),
120
+ ) -> None:
121
+ """Generate fresh secrets and write them to ``.env`` (or print them).
122
+
123
+ With ``--print`` nothing is written — the new values go to stdout so
124
+ you can pipe them into a secret manager. Otherwise the ``.env`` file
125
+ is rewritten in place (existing keys replaced, missing keys
126
+ appended) after a ``.env.bak`` backup.
127
+ """
128
+ names = [k.strip() for k in keys.split(",") if k.strip()]
129
+ if not names:
130
+ typer.echo("error: --keys produced no names.", err=True)
131
+ raise typer.Exit(2)
132
+
133
+ new_values = {name: _generate(length) for name in names}
134
+
135
+ if show:
136
+ for name, value in new_values.items():
137
+ typer.echo(f"{name}={value}")
138
+ return
139
+
140
+ path = Path(env_file).expanduser()
141
+ if path.is_file() and not no_backup:
142
+ backup = path.with_suffix(path.suffix + ".bak")
143
+ backup.write_text(path.read_text(encoding="utf-8"), encoding="utf-8")
144
+ typer.echo(f"Backed up {path} -> {backup}")
145
+
146
+ updated, appended = _rewrite_env(path, new_values)
147
+ for key in updated:
148
+ typer.echo(f"rotated {key}")
149
+ for key in appended:
150
+ typer.echo(f"added {key}")
151
+ typer.echo(
152
+ "Done. Rotating JWT_SECRET invalidates existing tokens — "
153
+ "restart the service to load the new values."
154
+ )
155
+
156
+
157
+ __all__: list[str] = [
158
+ "secrets_app",
159
+ ]
@@ -0,0 +1,107 @@
1
+ """Tests for ``tempest db seed``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import sys
7
+ import types
8
+ from collections.abc import Iterator
9
+ from pathlib import Path
10
+
11
+ import pytest
12
+ from sqlalchemy import String, select
13
+ from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
14
+ from sqlalchemy.orm import Mapped, mapped_column
15
+ from typer.testing import CliRunner
16
+
17
+ from tempest_fastapi_sdk import BaseModel
18
+ from tempest_fastapi_sdk.cli.main import app
19
+
20
+ runner = CliRunner()
21
+
22
+
23
+ class _SeedThingModel(BaseModel):
24
+ """Row inserted by the seed callable under test."""
25
+
26
+ __tablename__ = "seed_thing"
27
+
28
+ name: Mapped[str] = mapped_column(String(50), nullable=False)
29
+
30
+
31
+ async def _seed(session: AsyncSession) -> int:
32
+ """Async seed callable: insert two rows, return the count."""
33
+ session.add_all([_SeedThingModel(name="a"), _SeedThingModel(name="b")])
34
+ await session.flush()
35
+ return 2
36
+
37
+
38
+ def _sync_seed(session: AsyncSession) -> None:
39
+ """Sync seed callable: insert one row, return nothing."""
40
+ session.add(_SeedThingModel(name="sync"))
41
+
42
+
43
+ # Expose the callables via an importable dotted module.
44
+ _module = types.ModuleType("cli_seed_module")
45
+ _module.seed = _seed # type: ignore[attr-defined]
46
+ _module.sync_seed = _sync_seed # type: ignore[attr-defined]
47
+ sys.modules["cli_seed_module"] = _module
48
+
49
+
50
+ @pytest.fixture
51
+ def project_db(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Iterator[str]:
52
+ """Fresh on-disk SQLite with the seed table created."""
53
+ monkeypatch.chdir(tmp_path)
54
+ db_path = tmp_path / "app.db"
55
+ url = f"sqlite+aiosqlite:///{db_path}"
56
+ monkeypatch.setenv("DATABASE_URL", url)
57
+
58
+ async def _create_schema() -> None:
59
+ engine = create_async_engine(url)
60
+ async with engine.begin() as conn:
61
+ await conn.run_sync(BaseModel.metadata.create_all)
62
+ await engine.dispose()
63
+
64
+ asyncio.run(_create_schema())
65
+ yield url
66
+
67
+
68
+ def _count_rows(url: str) -> int:
69
+ async def _run() -> int:
70
+ engine = create_async_engine(url)
71
+ try:
72
+ async with AsyncSession(engine) as session:
73
+ result = await session.execute(select(_SeedThingModel))
74
+ return len(list(result.scalars().all()))
75
+ finally:
76
+ await engine.dispose()
77
+
78
+ return asyncio.run(_run())
79
+
80
+
81
+ class TestSeed:
82
+ def test_async_seed_runs_and_commits(self, project_db: str) -> None:
83
+ result = runner.invoke(app, ["db", "seed", "--seed", "cli_seed_module:seed"])
84
+ assert result.exit_code == 0, result.stdout + result.stderr
85
+ assert "(2 rows)" in result.stdout
86
+ assert _count_rows(project_db) == 2
87
+
88
+ def test_sync_seed_runs(self, project_db: str) -> None:
89
+ result = runner.invoke(
90
+ app, ["db", "seed", "--seed", "cli_seed_module:sync_seed"]
91
+ )
92
+ assert result.exit_code == 0, result.stdout + result.stderr
93
+ assert _count_rows(project_db) == 1
94
+
95
+
96
+ class TestSeedErrors:
97
+ def test_bad_spec_errors(self, project_db: str) -> None:
98
+ result = runner.invoke(app, ["db", "seed", "--seed", "nocolon"])
99
+ assert result.exit_code == 2
100
+
101
+ def test_not_callable_errors(self, project_db: str) -> None:
102
+ result = runner.invoke(app, ["db", "seed", "--seed", "cli_seed_module:__doc__"])
103
+ assert result.exit_code == 2
104
+
105
+ def test_missing_module_errors(self, project_db: str) -> None:
106
+ result = runner.invoke(app, ["db", "seed", "--seed", "no.such.module:seed"])
107
+ assert result.exit_code == 2