tempest-fastapi-sdk 0.35.0__tar.gz → 0.36.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 (361) hide show
  1. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/CHANGELOG.md +85 -0
  2. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/PKG-INFO +2 -2
  3. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/README.md +1 -1
  4. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/installation.en.md +1 -0
  5. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/installation.md +1 -0
  6. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/admin.en.md +25 -3
  7. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/admin.md +25 -3
  8. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/reference.md +10 -0
  9. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/roadmap.en.md +16 -1
  10. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/roadmap.md +16 -1
  11. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/pyproject.toml +1 -1
  12. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/__init__.py +1 -1
  13. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/admin/auth.py +85 -1
  14. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/admin/config.py +31 -0
  15. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/forms.py +363 -0
  16. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/router.py +1492 -0
  17. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/admin/session.py +17 -1
  18. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/static/admin.css +642 -0
  19. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/templates/dashboard.html +47 -0
  20. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/templates/detail.html +44 -0
  21. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/templates/form.html +53 -0
  22. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/templates/list.html +110 -0
  23. tempest_fastapi_sdk-0.36.0/tempest_fastapi_sdk/admin/templates/mfa.html +20 -0
  24. tempest_fastapi_sdk-0.36.0/tests/admin/test_forms.py +98 -0
  25. tempest_fastapi_sdk-0.36.0/tests/admin/test_mfa.py +170 -0
  26. tempest_fastapi_sdk-0.36.0/tests/admin/test_router.py +919 -0
  27. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/uv.lock +1 -1
  28. tempest_fastapi_sdk-0.35.0/tempest_fastapi_sdk/admin/router.py +0 -598
  29. tempest_fastapi_sdk-0.35.0/tempest_fastapi_sdk/admin/static/admin.css +0 -245
  30. tempest_fastapi_sdk-0.35.0/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -28
  31. tempest_fastapi_sdk-0.35.0/tempest_fastapi_sdk/admin/templates/detail.html +0 -18
  32. tempest_fastapi_sdk-0.35.0/tempest_fastapi_sdk/admin/templates/list.html +0 -62
  33. tempest_fastapi_sdk-0.35.0/tests/admin/test_router.py +0 -213
  34. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/.github/workflows/ci.yml +0 -0
  35. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/.github/workflows/docs.yml +0 -0
  36. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/.github/workflows/release-pypi.yml +0 -0
  37. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/.gitignore +0 -0
  38. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/.python-version +0 -0
  39. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/CLAUDE.md +0 -0
  40. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/Makefile +0 -0
  41. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/architecture.en.md +0 -0
  42. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/architecture.md +0 -0
  43. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/changelog.en.md +0 -0
  44. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/changelog.md +0 -0
  45. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/contributing.en.md +0 -0
  46. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/contributing.md +0 -0
  47. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/index.en.md +0 -0
  48. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/index.md +0 -0
  49. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/index.en.md +0 -0
  50. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/index.md +0 -0
  51. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/api.en.md +0 -0
  52. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/api.md +0 -0
  53. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/business-rules.en.md +0 -0
  54. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/business-rules.md +0 -0
  55. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/domain.en.md +0 -0
  56. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/domain.md +0 -0
  57. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/flows.en.md +0 -0
  58. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/flows.md +0 -0
  59. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/index.en.md +0 -0
  60. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/learning/marketplace/index.md +0 -0
  61. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/migration.en.md +0 -0
  62. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/migration.md +0 -0
  63. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/auth-flow.en.md +0 -0
  64. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/auth-flow.md +0 -0
  65. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/br-helpers.en.md +0 -0
  66. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/br-helpers.md +0 -0
  67. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/cache.en.md +0 -0
  68. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/cache.md +0 -0
  69. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/cli.en.md +0 -0
  70. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/cli.md +0 -0
  71. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/database.en.md +0 -0
  72. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/database.md +0 -0
  73. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/http.en.md +0 -0
  74. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/http.md +0 -0
  75. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/idempotency.en.md +0 -0
  76. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/idempotency.md +0 -0
  77. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/index.en.md +0 -0
  78. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/index.md +0 -0
  79. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/logging.en.md +0 -0
  80. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/logging.md +0 -0
  81. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/metrics.en.md +0 -0
  82. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/metrics.md +0 -0
  83. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/mfa.en.md +0 -0
  84. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/mfa.md +0 -0
  85. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/queue-tasks.en.md +0 -0
  86. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/queue-tasks.md +0 -0
  87. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/realtime.en.md +0 -0
  88. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/realtime.md +0 -0
  89. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/security.en.md +0 -0
  90. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/security.md +0 -0
  91. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/sessions.en.md +0 -0
  92. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/sessions.md +0 -0
  93. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/storage.en.md +0 -0
  94. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/storage.md +0 -0
  95. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/testing.en.md +0 -0
  96. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/testing.md +0 -0
  97. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/uploads.en.md +0 -0
  98. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/uploads.md +0 -0
  99. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/websocket.en.md +0 -0
  100. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/recipes/websocket.md +0 -0
  101. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/reference.en.md +0 -0
  102. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/tutorial.en.md +0 -0
  103. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/docs/tutorial.md +0 -0
  104. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/mkdocs.yml +0 -0
  105. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/scripts/extract_recipe.py +0 -0
  106. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/admin/__init__.py +0 -0
  107. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/admin/site.py +0 -0
  108. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/admin/templates/base.html +0 -0
  109. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
  110. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
  111. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
  112. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
  113. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/dependencies/auth.py +0 -0
  114. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
  115. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
  116. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
  117. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
  118. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
  119. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
  120. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
  121. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
  122. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
  123. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
  124. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
  125. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
  126. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
  127. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
  128. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/server.py +0 -0
  129. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/static.py +0 -0
  130. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
  131. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
  132. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
  133. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/router.py +0 -0
  134. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
  135. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/service.py +0 -0
  136. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/templates/activation.html +0 -0
  137. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/templates/activation_error.html +0 -0
  138. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/templates/activation_success.html +0 -0
  139. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/templates/password_reset.html +0 -0
  140. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/templates/password_reset_error.html +0 -0
  141. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/templates/password_reset_form.html +0 -0
  142. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/auth/templates/password_reset_success.html +0 -0
  143. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
  144. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
  145. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
  146. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
  147. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
  148. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +0 -0
  149. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
  150. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
  151. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
  152. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
  153. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
  154. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +0 -0
  155. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/__init__.py.tmpl +0 -0
  156. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/auth.py.tmpl +0 -0
  157. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/api/routers/__init__.py.tmpl +0 -0
  158. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
  159. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
  160. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
  161. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -0
  162. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
  163. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/__init__.py.tmpl +0 -0
  164. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/user.py.tmpl +0 -0
  165. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/db/repositories/__init__.py.tmpl +0 -0
  166. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
  167. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
  168. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
  169. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
  170. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
  171. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
  172. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/db.py +0 -0
  173. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
  174. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
  175. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
  176. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/main.py +0 -0
  177. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/new.py +0 -0
  178. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/cli/user.py +0 -0
  179. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
  180. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
  181. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
  182. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/core/context.py +0 -0
  183. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/core/enums.py +0 -0
  184. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/core/logging.py +0 -0
  185. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/__init__.py +0 -0
  186. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
  187. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
  188. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
  189. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/connection.py +0 -0
  190. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
  191. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
  192. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/model.py +0 -0
  193. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/repository.py +0 -0
  194. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
  195. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
  196. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
  197. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
  198. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
  199. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
  200. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
  201. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
  202. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
  203. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
  204. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
  205. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
  206. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
  207. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/py.typed +0 -0
  208. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
  209. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
  210. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
  211. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
  212. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
  213. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
  214. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/schemas/pagination.py +0 -0
  215. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
  216. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
  217. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/services/base.py +0 -0
  218. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
  219. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
  220. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
  221. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
  222. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
  223. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
  224. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
  225. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
  226. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/settings/base.py +0 -0
  227. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
  228. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sse/__init__.py +0 -0
  229. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
  230. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
  231. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
  232. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
  233. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
  234. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
  235. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
  236. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/testing/database.py +0 -0
  237. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
  238. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
  239. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
  240. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
  241. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/download.py +0 -0
  242. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/email.py +0 -0
  243. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
  244. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
  245. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/log.py +0 -0
  246. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
  247. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
  248. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/password.py +0 -0
  249. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
  250. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
  251. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
  252. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
  253. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
  254. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
  255. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
  256. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
  257. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
  258. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
  259. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
  260. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
  261. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/__init__.py +0 -0
  262. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/admin/__init__.py +0 -0
  263. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/admin/test_auth.py +0 -0
  264. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/admin/test_site.py +0 -0
  265. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/admin/test_user_model.py +0 -0
  266. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/__init__.py +0 -0
  267. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_body_size.py +0 -0
  268. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_cookies.py +0 -0
  269. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_cors.py +0 -0
  270. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_csrf.py +0 -0
  271. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_dependencies_auth.py +0 -0
  272. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_handlers.py +0 -0
  273. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_health_router.py +0 -0
  274. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_idempotency.py +0 -0
  275. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_jwt_dependency.py +0 -0
  276. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_logs_router.py +0 -0
  277. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_oauth.py +0 -0
  278. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_prometheus.py +0 -0
  279. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_rate_limit.py +0 -0
  280. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_request_id_middleware.py +0 -0
  281. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_role_dependency.py +0 -0
  282. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_server.py +0 -0
  283. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_static.py +0 -0
  284. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_tool_spec.py +0 -0
  285. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_webhooks.py +0 -0
  286. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/api/test_webhooks_rsa.py +0 -0
  287. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/auth/__init__.py +0 -0
  288. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/auth/test_mfa.py +0 -0
  289. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/auth/test_service.py +0 -0
  290. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cache/__init__.py +0 -0
  291. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cache/test_decorator.py +0 -0
  292. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cache/test_redis_manager.py +0 -0
  293. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cli/__init__.py +0 -0
  294. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cli/test_db.py +0 -0
  295. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cli/test_docker_compose.py +0 -0
  296. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cli/test_generate.py +0 -0
  297. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cli/test_main.py +0 -0
  298. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/cli/test_user.py +0 -0
  299. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/conftest.py +0 -0
  300. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/controllers/__init__.py +0 -0
  301. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/controllers/test_base.py +0 -0
  302. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/core/__init__.py +0 -0
  303. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/core/test_context.py +0 -0
  304. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/core/test_enums.py +0 -0
  305. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/core/test_logging.py +0 -0
  306. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/__init__.py +0 -0
  307. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/test_alembic_hooks.py +0 -0
  308. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/test_bulk_ops.py +0 -0
  309. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/test_connection.py +0 -0
  310. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/test_migrations.py +0 -0
  311. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/test_mixins.py +0 -0
  312. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/test_model.py +0 -0
  313. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/db/test_repository.py +0 -0
  314. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/exceptions/__init__.py +0 -0
  315. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/exceptions/test_exceptions.py +0 -0
  316. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/queue/__init__.py +0 -0
  317. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/queue/test_manager.py +0 -0
  318. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/schemas/__init__.py +0 -0
  319. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/schemas/test_base.py +0 -0
  320. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/schemas/test_cursor_pagination.py +0 -0
  321. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/schemas/test_link_headers.py +0 -0
  322. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/schemas/test_pagination.py +0 -0
  323. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/schemas/test_response.py +0 -0
  324. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/services/__init__.py +0 -0
  325. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/services/test_base.py +0 -0
  326. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/sessions/__init__.py +0 -0
  327. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/sessions/test_sessions.py +0 -0
  328. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/settings/__init__.py +0 -0
  329. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/settings/test_base.py +0 -0
  330. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/settings/test_mixins.py +0 -0
  331. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/sse/__init__.py +0 -0
  332. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/sse/test_event_stream.py +0 -0
  333. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/storage/__init__.py +0 -0
  334. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/storage/test_minio_client.py +0 -0
  335. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/tasks/__init__.py +0 -0
  336. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/tasks/test_manager.py +0 -0
  337. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/tasks/test_scheduler.py +0 -0
  338. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/testing/__init__.py +0 -0
  339. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/testing/test_database.py +0 -0
  340. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/__init__.py +0 -0
  341. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_client_ip.py +0 -0
  342. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_datetime.py +0 -0
  343. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_dict.py +0 -0
  344. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_download.py +0 -0
  345. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_email.py +0 -0
  346. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_http_client.py +0 -0
  347. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_jwt.py +0 -0
  348. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_lazy_extras.py +0 -0
  349. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_log.py +0 -0
  350. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_metrics.py +0 -0
  351. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_opaque_token.py +0 -0
  352. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_password.py +0 -0
  353. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_regex.py +0 -0
  354. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_storage_backends.py +0 -0
  355. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_throttle.py +0 -0
  356. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/utils/test_upload.py +0 -0
  357. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/webpush/__init__.py +0 -0
  358. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/webpush/test_dispatcher.py +0 -0
  359. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/webpush/test_schemas.py +0 -0
  360. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/websockets/__init__.py +0 -0
  361. {tempest_fastapi_sdk-0.35.0 → tempest_fastapi_sdk-0.36.0}/tests/websockets/test_hub_and_router.py +0 -0
@@ -5,6 +5,91 @@ 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.36.0] — 2026-06-06
9
+
10
+ Admin panel brought to Django-admin parity: the list view, write CRUD,
11
+ bulk actions, dashboard, login, and audit trail all landed across the
12
+ phases below.
13
+
14
+ ### Added
15
+
16
+ - **Admin list view — Phase 1 (read-only enhancements + responsive).**
17
+ - Clickable **column sorting** on the list view
18
+ (`?sort=<column>&dir=asc|desc`), validated against the displayed
19
+ real columns; the admin's configured `ordering` remains the
20
+ default.
21
+ - **CSV / JSON export** endpoint
22
+ (`GET /admin/m/{slug}/export.csv` / `.json`) honoring the active
23
+ search / filters / sort. New `make_admin_router(export_max_rows=…)`
24
+ caps export size (default 5000).
25
+ - **Responsive admin UI** — bundled templates + CSS now adapt to
26
+ mobile (≤600px): stacked header, full-width search/filters/actions,
27
+ horizontal-scroll table wrappers, single-column detail grid.
28
+ Verified at 390px (mobile) and 1280px (desktop).
29
+
30
+ - **Admin write CRUD — Phase 2a (create / edit / delete).**
31
+ - `GET/POST /admin/m/{slug}/new` (create), `GET/POST
32
+ /admin/m/{slug}/{identity}/edit` (edit), and `POST
33
+ /admin/m/{slug}/{identity}/delete` (delete), each gated by new
34
+ `AdminModel(can_create=…, can_edit=…, can_delete=…)` flags
35
+ (default `True`; a disabled view returns `404`).
36
+ - **CSRF-protected** mutations — every write form carries the
37
+ session CSRF token, verified server-side (`403` on mismatch).
38
+ - **Type-aware field widgets** — text / textarea (long strings) /
39
+ number / checkbox / `datetime-local` / date / enum `select`,
40
+ derived from the column types, with required-field + per-field
41
+ validation errors re-rendered on the form, and integrity errors
42
+ surfaced inline.
43
+ - Detail view gains Edit / Delete controls; list view gains a
44
+ "+ New" button. All responsive (verified at 390px / 1280px).
45
+
46
+ - **Admin bulk actions — Phase 2b.**
47
+ - List view gains row checkboxes + a select-all toggle and a bulk
48
+ action bar. `POST /admin/m/{slug}/bulk` applies **delete**
49
+ (`can_delete`), **activate** / **deactivate** (`can_edit`, toggling
50
+ the `is_active` flag) to the selected rows, CSRF-verified.
51
+ Backed by `BaseRepository.delete_batch` / `bulk_update`.
52
+ Responsive (verified at 390px / 1280px).
53
+
54
+ - **Admin foreign-key select — Phase 2c.**
55
+ - A foreign-key column whose referenced table has its own
56
+ `AdminModel` now renders as a **dropdown of the related rows**
57
+ (Django's FK select) on the create/edit form, instead of a raw
58
+ UUID input. Option labels come from the referenced admin's first
59
+ `search_fields` entry (falling back to a `name`/`title`/`email`
60
+ attribute, then the id). Capped at 1000 rows. FKs to unmanaged
61
+ tables stay plain UUID inputs.
62
+
63
+ - **Admin dashboard — counts + metrics (Phase 3a).**
64
+ - The dashboard now renders each registered model as a card with its
65
+ **live row count** and Browse / + New links, plus a **system
66
+ metrics panel** (CPU / RAM / disk) via `MetricsUtils`. The panel
67
+ is on by default, silently omitted when the `[metrics]` extra is
68
+ absent, and disabled with `make_admin_router(show_metrics=False)`.
69
+ Responsive card grids (verified at 390px / 1280px).
70
+
71
+ - **Admin MFA login — Phase 3b.**
72
+ - The admin login now supports a TOTP second factor. After the
73
+ password step, a principal with MFA enabled gets a short
74
+ `mfa_pending` session and is redirected to `GET/POST /admin/mfa`
75
+ (a CSRF-protected TOTP challenge); only a valid code upgrades the
76
+ session to full access. `AdminAuthBackend` gains `mfa_enabled` /
77
+ `verify_mfa` (default off); `UserModelAuthBackend` implements them
78
+ against `MFAMixin`'s `totp_secret` / `totp_enabled_at` via
79
+ `TOTPHelper` (new `mfa_issuer` / `mfa_window` ctor args). Pending
80
+ sessions are denied every admin page until the challenge passes.
81
+
82
+ - **Admin audit trail — Phase 3c.**
83
+ - Create/edit through the admin now **stamps** `created_by` /
84
+ `updated_by` (from `AuditMixin`) with the acting admin's id. The
85
+ detail view gained an **Audit panel** showing created/updated
86
+ timestamps and — when the model has the audit columns — the actor,
87
+ with the stored UUID resolved to a display name via the auth
88
+ backend. Models without `AuditMixin` show the timestamps only.
89
+
90
+ File-upload widget and inline/related editing remain tracked as later
91
+ admin phases on the roadmap.
92
+
8
93
  ## [0.35.0] — 2026-06-06
9
94
 
10
95
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tempest-fastapi-sdk
3
- Version: 0.35.0
3
+ Version: 0.36.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
@@ -174,7 +174,7 @@ Via `pyproject.toml`:
174
174
 
175
175
  ```toml
176
176
  dependencies = [
177
- "tempest-fastapi-sdk>=0.35.0",
177
+ "tempest-fastapi-sdk>=0.36.0",
178
178
  ]
179
179
  ```
180
180
 
@@ -89,7 +89,7 @@ Via `pyproject.toml`:
89
89
 
90
90
  ```toml
91
91
  dependencies = [
92
- "tempest-fastapi-sdk>=0.35.0",
92
+ "tempest-fastapi-sdk>=0.36.0",
93
93
  ]
94
94
  ```
95
95
 
@@ -29,6 +29,7 @@ Feature-rich helpers pull in third-party dependencies that you only need when yo
29
29
  | `[minio]` | `minio` | `AsyncMinIOClient`, `MinIOUploadStorage` |
30
30
  | `[http]` | `httpx` | `HTTPClient` + `RetryPolicy` + circuit-breaker |
31
31
  | `[prometheus]` | `prometheus-client` | `PrometheusMiddleware`, `make_prometheus_router`, `make_prometheus_registry` |
32
+ | `[mfa]` | `pyotp` | `TOTPHelper` + MFA/2FA (TOTP) endpoints on the bundled auth flow |
32
33
  | `[all]` | everything above | every helper |
33
34
 
34
35
  === "Subset (recommended)"
@@ -29,6 +29,7 @@ Os helpers mais ricos puxam dependências de terceiros que só são necessárias
29
29
  | `[minio]` | `minio` | `AsyncMinIOClient`, `MinIOUploadStorage` |
30
30
  | `[http]` | `httpx` | `HTTPClient` + `RetryPolicy` + circuit-breaker |
31
31
  | `[prometheus]` | `prometheus-client` | `PrometheusMiddleware`, `make_prometheus_router`, `make_prometheus_registry` |
32
+ | `[mfa]` | `pyotp` | `TOTPHelper` + endpoints MFA/2FA (TOTP) do fluxo bundled de auth |
32
33
  | `[all]` | tudo acima | todos os helpers |
33
34
 
34
35
  === "Subconjunto (recomendado)"
@@ -120,11 +120,33 @@ app.include_router(
120
120
  `make_admin_router` mounts:
121
121
 
122
122
  - `GET /admin/login`, `POST /admin/login`, `POST /admin/logout` — auth flow.
123
- - `GET /admin/`dashboard listing every registered admin.
124
- - `GET /admin/m/{slug}/` — list view with pagination + free-text search (`?q=`) + per-field filters (`?filter_<field>=value`).
125
- - `GET /admin/m/{slug}/{identity}`read-only detail view.
123
+ - `GET/POST /admin/mfa`TOTP second-factor challenge between the password step and access, for MFA-enabled principals.
124
+ - `GET /admin/` — dashboard: a card per model with its **live row count** + Browse/New, plus a **metrics panel** (CPU/RAM/disk via `MetricsUtils`). On by default, omitted without the `[metrics]` extra, disable with `make_admin_router(show_metrics=False)`.
125
+ - `GET /admin/m/{slug}/`list view with pagination + free-text search (`?q=`) + per-field filters (`?filter_<field>=value`) + clickable **column sorting** (`?sort=<column>&dir=asc|desc`).
126
+ - `GET /admin/m/{slug}/export.csv` / `export.json` — **export** the current result set (honoring search/filters/sort) as CSV or JSON. Row cap via `make_admin_router(export_max_rows=…)` (default 5000).
127
+ - `POST /admin/m/{slug}/bulk` — **bulk actions** (delete / activate / deactivate) on the selected rows.
128
+ - `GET/POST /admin/m/{slug}/new` — **create** a record (when `can_create`).
129
+ - `GET /admin/m/{slug}/{identity}` — detail view with Edit/Delete controls.
130
+ - `GET/POST /admin/m/{slug}/{identity}/edit` — **edit** a record (when `can_edit`).
131
+ - `POST /admin/m/{slug}/{identity}/delete` — **delete** a record (when `can_delete`).
126
132
  - `GET /admin/static/{path}` — bundled CSS/HTMX assets.
127
133
 
134
+ !!! info "Write CRUD + permissions"
135
+ Create/edit/delete are gated by `AdminModel` flags: `can_create` / `can_edit` / `can_delete` (all `True` by default; a disabled view returns `404`). Every write POST carries the session CSRF token, verified server-side (`403` on mismatch). **Field widgets** are derived from the column type — text / textarea (long strings) / number / checkbox / `datetime-local` / date / `select` for enums — with required-field + per-field validation errors re-rendered on the form.
136
+
137
+ **Bulk actions**: the list view shows per-row checkboxes + select-all and an action bar (delete / activate / deactivate) operating on the checked rows via `POST .../bulk` (CSRF + `can_delete`/`can_edit` flags), backed by `BaseRepository.delete_batch` / `bulk_update`.
138
+
139
+ **FK-select**: a foreign-key column whose target has a registered `AdminModel` renders as a dropdown of the related rows (like Django's FK select) on the form, instead of a raw UUID input. The option label comes from the referenced admin's first `search_fields` entry (fallback: a `name`/`title`/`email` attribute, then the id). Capped at 1000 rows; FKs to unmanaged tables stay UUID inputs.
140
+
141
+ **MFA login**: a principal with MFA enabled (`MFAMixin`'s `totp_secret`/`totp_enabled_at`) goes through a TOTP challenge at `/admin/mfa` after the password — only a valid code grants access. Enable it via `UserModelAuthBackend(UserModel, mfa_issuer=...)`; custom backends override `mfa_enabled`/`verify_mfa`.
142
+
143
+ **Audit trail**: create/edit through the admin stamps `created_by`/`updated_by` (from `AuditMixin`) with the acting admin's id; the detail view shows an **Audit** panel with timestamps and — when the model has the audit columns — the actor (UUID resolved to a display name via the auth backend). Models without `AuditMixin` show timestamps only.
144
+
145
+ Not yet included (later roadmap phases): file upload, inline/related editing.
146
+
147
+ !!! tip "Responsive by default"
148
+ The bundled templates + CSS are responsive: on narrow screens (≤600px) the header stacks, search/filters/actions go full-width, tables get horizontal scroll (never breaking the layout), and the detail grid collapses to a single column. Column headers are clickable to toggle sort order (▲/▼).
149
+
128
150
  #### 4. Session security defaults
129
151
 
130
152
  `SignedCookieSessionStore` uses `itsdangerous.TimestampSigner` (HMAC-SHA256) to sign a single cookie:
@@ -120,11 +120,33 @@ app.include_router(
120
120
  `make_admin_router` monta:
121
121
 
122
122
  - `GET /admin/login`, `POST /admin/login`, `POST /admin/logout` — fluxo de auth.
123
- - `GET /admin/`dashboard listando todo admin registrado.
124
- - `GET /admin/m/{slug}/` — list view com paginação + busca em texto livre (`?q=`) + filtros por campo (`?filter_<field>=value`).
125
- - `GET /admin/m/{slug}/{identity}`detail view somente leitura.
123
+ - `GET/POST /admin/mfa`desafio TOTP (segundo fator) entre a senha e o acesso, para principais com MFA habilitado.
124
+ - `GET /admin/` — dashboard: card por modelo com **contagem de linhas** + Browse/New, e um **painel de métricas** (CPU/RAM/disco via `MetricsUtils`). Painel ligado por default, omitido sem o extra `[metrics]`, desligável com `make_admin_router(show_metrics=False)`.
125
+ - `GET /admin/m/{slug}/`list view com paginação + busca em texto livre (`?q=`) + filtros por campo (`?filter_<field>=value`) + **ordenação por coluna** clicável (`?sort=<coluna>&dir=asc|desc`).
126
+ - `GET /admin/m/{slug}/export.csv` / `export.json` — **exporta** o resultado atual (respeitando busca/filtros/ordenação) como CSV ou JSON. Limite de linhas via `make_admin_router(export_max_rows=…)` (default 5000).
127
+ - `POST /admin/m/{slug}/bulk` — **ações em massa** (delete / activate / deactivate) nas linhas selecionadas.
128
+ - `GET/POST /admin/m/{slug}/new` — **criar** registro (quando `can_create`).
129
+ - `GET /admin/m/{slug}/{identity}` — detail view com botões Edit/Delete.
130
+ - `GET/POST /admin/m/{slug}/{identity}/edit` — **editar** registro (quando `can_edit`).
131
+ - `POST /admin/m/{slug}/{identity}/delete` — **excluir** registro (quando `can_delete`).
126
132
  - `GET /admin/static/{path}` — assets CSS/HTMX embutidos.
127
133
 
134
+ !!! info "Escrita (CRUD) + permissões"
135
+ Create/edit/delete são controlados por flags no `AdminModel`: `can_create` / `can_edit` / `can_delete` (todas `True` por default; uma view desativada responde `404`). Todo POST de escrita carrega o token CSRF da sessão, validado no servidor (`403` em mismatch). Os **widgets de campo** são derivados do tipo da coluna — texto / textarea (strings longas) / number / checkbox / `datetime-local` / date / `select` para enums — com validação de obrigatórios + erros por campo re-renderizados no formulário.
136
+
137
+ **Ações em massa**: a list view mostra checkboxes por linha + select-all e uma barra de ação (delete / activate / deactivate) que opera nas linhas marcadas via `POST .../bulk` (CSRF + flags `can_delete`/`can_edit`), apoiada em `BaseRepository.delete_batch` / `bulk_update`.
138
+
139
+ **FK-select**: uma coluna FK cujo destino tem `AdminModel` registrado vira um dropdown das linhas relacionadas (igual ao FK select do Django) no formulário, em vez de um input UUID cru. O label da opção vem do primeiro `search_fields` do admin referenciado (fallback: atributo `name`/`title`/`email`, depois o id). Limitado a 1000 linhas; FK para tabela não-gerenciada continua input UUID.
140
+
141
+ **MFA no login**: um principal com MFA habilitado (colunas `totp_secret`/`totp_enabled_at` do `MFAMixin`) passa por um desafio TOTP em `/admin/mfa` depois da senha — só um código válido libera o acesso. Habilite passando um usuário com MFA via `UserModelAuthBackend(UserModel, mfa_issuer=...)`; backends customizados sobrescrevem `mfa_enabled`/`verify_mfa`.
142
+
143
+ **Audit trail**: create/edit pelo admin carimba `created_by`/`updated_by` (do `AuditMixin`) com o id do admin atuante; o detail mostra um painel **Audit** com timestamps e — quando o modelo tem as colunas de auditoria — o ator (UUID resolvido para nome via o auth backend). Modelos sem `AuditMixin` mostram só os timestamps.
144
+
145
+ Ainda **não** incluídos (fases futuras do roadmap): upload de arquivo, inline/related editing.
146
+
147
+ !!! tip "Responsivo por padrão"
148
+ Os templates + CSS embutidos são responsivos: em telas estreitas (≤600px) o header empilha, busca/filtros/ações viram full-width, as tabelas ganham scroll horizontal (nunca quebram o layout) e o grid do detail colapsa para uma coluna. Headers de coluna são clicáveis para alternar a ordenação (▲/▼).
149
+
128
150
  #### 4. Defaults de segurança de sessão
129
151
 
130
152
  `SignedCookieSessionStore` usa `itsdangerous.TimestampSigner` (HMAC-SHA256) para assinar um único cookie:
@@ -26,9 +26,14 @@ Gerada automaticamente a partir das docstrings do SDK via [`mkdocstrings`](https
26
26
 
27
27
  ::: tempest_fastapi_sdk.db.model.BaseModel
28
28
  ::: tempest_fastapi_sdk.db.user_model.BaseUserModel
29
+ ::: tempest_fastapi_sdk.db.user_token_model.BaseUserTokenModel
30
+ ::: tempest_fastapi_sdk.db.user_token_model.make_user_token_model
31
+ ::: tempest_fastapi_sdk.db.user_recovery_code_model.BaseUserRecoveryCodeModel
32
+ ::: tempest_fastapi_sdk.db.user_recovery_code_model.make_user_recovery_code_model
29
33
  ::: tempest_fastapi_sdk.db.repository.BaseRepository
30
34
  ::: tempest_fastapi_sdk.db.mixins.SoftDeleteMixin
31
35
  ::: tempest_fastapi_sdk.db.mixins.AuditMixin
36
+ ::: tempest_fastapi_sdk.db.mixins.MFAMixin
32
37
  ::: tempest_fastapi_sdk.db.connection.AsyncDatabaseManager
33
38
  ::: tempest_fastapi_sdk.db.migrations.AlembicHelper
34
39
 
@@ -120,6 +125,10 @@ Gerada automaticamente a partir das docstrings do SDK via [`mkdocstrings`](https
120
125
  ::: tempest_fastapi_sdk.auth.schemas.PasswordResetConfirmSchema
121
126
  ::: tempest_fastapi_sdk.auth.schemas.ActivationToken
122
127
  ::: tempest_fastapi_sdk.auth.schemas.PasswordResetToken
128
+ ::: tempest_fastapi_sdk.auth.schemas.MFAEnrollResponseSchema
129
+ ::: tempest_fastapi_sdk.auth.schemas.MFAConfirmSchema
130
+ ::: tempest_fastapi_sdk.auth.schemas.MFAVerifySchema
131
+ ::: tempest_fastapi_sdk.auth.schemas.MFADisableSchema
123
132
 
124
133
  ### `tempest_fastapi_sdk.sessions`
125
134
 
@@ -211,6 +220,7 @@ Gerada automaticamente a partir das docstrings do SDK via [`mkdocstrings`](https
211
220
 
212
221
  ::: tempest_fastapi_sdk.utils.password.PasswordUtils
213
222
  ::: tempest_fastapi_sdk.utils.jwt.JWTUtils
223
+ ::: tempest_fastapi_sdk.utils.totp.TOTPHelper
214
224
  ::: tempest_fastapi_sdk.utils.email.EmailUtils
215
225
  ::: tempest_fastapi_sdk.utils.upload.UploadUtils
216
226
  ::: tempest_fastapi_sdk.utils.metrics.MetricsUtils
@@ -32,7 +32,7 @@ What the SDK **doesn't ship yet** + what already landed. Sorted by impact, not i
32
32
  | 2FA / TOTP (`pyotp` wrapper + recovery codes) | ✅ v0.35.0 | `TOTPHelper` + `UserAuthService.mfa_*` + `BaseUserRecoveryCodeModel` |
33
33
  | `tempest db` + `tempest user` CLI | ✅ v0.30.0 | `tempest_fastapi_sdk.cli.db` / `cli.user` |
34
34
  | `BaseRepository.bulk_update` (filters + values) | ✅ pre-existing | `BaseRepository.bulk_update` |
35
- | **Multi-tenant scope** — `TenantScopedRepository(tenant_id)` auto-injecting `WHERE tenant_id = …` on every repository query | ❌ planned v0.36.0 | — |
35
+ | **Multi-tenant scope** — `TenantScopedRepository(tenant_id)` auto-injecting `WHERE tenant_id = …` on every repository query | ❌ planned v0.37.0 | — |
36
36
 
37
37
  ## Tier B — when the service grows
38
38
 
@@ -50,6 +50,21 @@ What the SDK **doesn't ship yet** + what already landed. Sorted by impact, not i
50
50
  | `tempest db seed` — load JSON/Python fixtures | ❌ pending |
51
51
  | CLI: `tempest secrets rotate` | ❌ pending |
52
52
 
53
+ ## Admin panel — evolution
54
+
55
+ The admin panel already exists (`AdminSite` / `AdminModel` / `make_admin_router`, Jinja + HTMX, CSRF token). The items below take it from "functional CRUD" to "production admin", reusing primitives the SDK already ships (`AuditMixin`, `MetricsUtils`, `TOTPHelper`, `UploadUtils`).
56
+
57
+ | Feature | Why it matters | Reuses |
58
+ |---------|----------------|--------|
59
+ | **Per-column filter / search / sort** on the list view | Large lists are unusable without it — the first thing every operator asks for. | `BaseRepository` (filters + pagination) |
60
+ | **Bulk actions** (mass delete / activate) | Row-by-row actions don't scale; select N rows + one action is the standard admin flow. | `BaseRepository.bulk_update` / soft-delete |
61
+ | **Field widgets** (FK select, date picker, file upload) | The form is generic today; FK as `<select>`, dates with a picker, and upload via `UploadUtils` remove manual typing and error. | `UploadUtils` + storage backends |
62
+ | **Inline / related editing** | Edit children (1-N) on the parent's screen — the Django-admin pattern that's missing. | `BaseRepository` + relationships |
63
+ | **CSV / JSON export** | Operator exports the filtered result without opening the database. | list view + filters |
64
+ | **Audit log visible in the admin** | Who changed what and when, straight in the UI. | `AuditMixin` (`created_by` / `updated_by`) |
65
+ | **Metrics dashboard** | A landing screen with CPU/RAM/counters instead of an empty page. | `MetricsUtils` |
66
+ | **MFA on admin login** | Second factor on the most sensitive access in the system; a natural fit now that TOTP exists. | `TOTPHelper` + `MFAMixin` + recovery codes |
67
+
53
68
  ## Everything shipped so far
54
69
 
55
70
  ### ✅ v0.23.0 — MinIO/S3 storage
@@ -32,7 +32,7 @@ Esta página lista o que o SDK **ainda não oferece** + o que já foi entregue.
32
32
  | 2FA / TOTP (`pyotp` wrapper + recovery codes) | ✅ v0.35.0 | `TOTPHelper` + `UserAuthService.mfa_*` + `BaseUserRecoveryCodeModel` |
33
33
  | `tempest db` + `tempest user` CLI | ✅ v0.30.0 | `tempest_fastapi_sdk.cli.db` / `cli.user` |
34
34
  | `BaseRepository.bulk_update` (filters + values) | ✅ pré-existente | `BaseRepository.bulk_update` |
35
- | **Escopo multi-tenant** — `TenantScopedRepository(tenant_id)` auto-injetando `WHERE tenant_id = …` em toda query do repository | ❌ planejado v0.36.0 | — |
35
+ | **Escopo multi-tenant** — `TenantScopedRepository(tenant_id)` auto-injetando `WHERE tenant_id = …` em toda query do repository | ❌ planejado v0.37.0 | — |
36
36
 
37
37
  ## Tier B — quando o serviço crescer
38
38
 
@@ -50,6 +50,21 @@ Esta página lista o que o SDK **ainda não oferece** + o que já foi entregue.
50
50
  | `tempest db seed` — carregar fixtures JSON/Python | ❌ pendente |
51
51
  | CLI: `tempest secrets rotate` | ❌ pendente |
52
52
 
53
+ ## Painel admin — evolução
54
+
55
+ O painel admin já existe (`AdminSite` / `AdminModel` / `make_admin_router`, Jinja + HTMX, CSRF token). Os itens abaixo elevam ele de "CRUD funcional" para "admin de produção", reaproveitando primitivos que o SDK já tem (`AuditMixin`, `MetricsUtils`, `TOTPHelper`, `UploadUtils`).
56
+
57
+ | Feature | Por que importa | Reaproveita |
58
+ |---------|-----------------|-------------|
59
+ | **Filtros / busca / ordenação por coluna** na listagem | Listas grandes ficam inutilizáveis sem isso; é o primeiro pedido de todo operador. | `BaseRepository` (filtros + paginação) |
60
+ | **Bulk actions** (deletar / ativar em massa) | Ações linha-a-linha não escalam; selecionar N linhas + uma ação é o fluxo padrão de admin. | `BaseRepository.bulk_update` / soft-delete |
61
+ | **Widgets de campo** (FK select, date picker, file upload) | Hoje o form é genérico; FK como `<select>`, data com picker e upload via `UploadUtils` removem digitação manual e erro. | `UploadUtils` + storage backends |
62
+ | **Inline / related editing** | Editar filhos (1-N) na mesma tela do pai — padrão Django admin que falta. | `BaseRepository` + relationships |
63
+ | **Export CSV / JSON** | Operador exporta o resultado filtrado sem abrir o banco. | listagem + filtros |
64
+ | **Audit log visível no admin** | Quem mudou o quê e quando, direto na UI. | `AuditMixin` (`created_by` / `updated_by`) |
65
+ | **Dashboard com métricas** | Tela inicial com CPU/RAM/contadores em vez de página vazia. | `MetricsUtils` |
66
+ | **MFA no login do admin** | Segundo fator no acesso mais sensível do sistema; encaixe natural agora que o TOTP existe. | `TOTPHelper` + `MFAMixin` + recovery codes |
67
+
53
68
  ## Tudo que já entregamos
54
69
 
55
70
  ### ✅ v0.23.0 — Storage MinIO/S3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tempest-fastapi-sdk"
3
- version = "0.35.0"
3
+ version = "0.36.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"
@@ -242,7 +242,7 @@ from tempest_fastapi_sdk.websockets import (
242
242
  make_websocket_router,
243
243
  )
244
244
 
245
- __version__: str = "0.35.0"
245
+ __version__: str = "0.36.0"
246
246
 
247
247
  __all__: list[str] = [
248
248
  "BASE_COLUMN_ORDER",
@@ -123,6 +123,37 @@ class AdminAuthBackend(ABC):
123
123
  email = getattr(principal, "email", None)
124
124
  return str(email) if email else repr(principal)
125
125
 
126
+ def mfa_enabled(self, principal: Any) -> bool:
127
+ """Whether ``principal`` must pass a TOTP challenge after login.
128
+
129
+ Defaults to ``False`` so non-user backends (LDAP, OAuth) skip
130
+ the second factor unless they opt in. Override to gate the
131
+ login behind MFA.
132
+
133
+ Args:
134
+ principal (Any): The authenticated principal.
135
+
136
+ Returns:
137
+ bool: ``True`` to require the TOTP step.
138
+ """
139
+ return False
140
+
141
+ def verify_mfa(self, principal: Any, code: str) -> bool:
142
+ """Verify a submitted TOTP ``code`` for ``principal``.
143
+
144
+ Only called when :meth:`mfa_enabled` returned ``True``. The
145
+ default rejects everything; backends that enable MFA must
146
+ override it.
147
+
148
+ Args:
149
+ principal (Any): The authenticated principal.
150
+ code (str): The 6-digit code from the authenticator app.
151
+
152
+ Returns:
153
+ bool: ``True`` when the code is valid.
154
+ """
155
+ return False
156
+
126
157
 
127
158
  class UserModelAuthBackend(AdminAuthBackend):
128
159
  """Default backend backed by :class:`BaseUserModel`.
@@ -138,11 +169,20 @@ class UserModelAuthBackend(AdminAuthBackend):
138
169
  Must be a subclass of :class:`BaseUserModel`.
139
170
  """
140
171
 
141
- def __init__(self, user_model: type[BaseUserModel]) -> None:
172
+ def __init__(
173
+ self,
174
+ user_model: type[BaseUserModel],
175
+ *,
176
+ mfa_issuer: str = "Admin",
177
+ mfa_window: int = 1,
178
+ ) -> None:
142
179
  """Initialize the backend.
143
180
 
144
181
  Args:
145
182
  user_model (type[BaseUserModel]): The user model to query.
183
+ mfa_issuer (str): Issuer label used when verifying TOTP
184
+ codes (matches what the authenticator app stored).
185
+ mfa_window (int): TOTP clock-drift tolerance in 30s steps.
146
186
 
147
187
  Raises:
148
188
  TypeError: When ``user_model`` is not a subclass of
@@ -155,6 +195,8 @@ class UserModelAuthBackend(AdminAuthBackend):
155
195
  "user_model must be a subclass of BaseUserModel",
156
196
  )
157
197
  self.user_model: type[BaseUserModel] = user_model
198
+ self.mfa_issuer: str = mfa_issuer
199
+ self.mfa_window: int = mfa_window
158
200
 
159
201
  async def authenticate(
160
202
  self,
@@ -235,6 +277,48 @@ class UserModelAuthBackend(AdminAuthBackend):
235
277
  """
236
278
  return str(principal.id)
237
279
 
280
+ def mfa_enabled(self, principal: Any) -> bool:
281
+ """Whether the user has completed MFA enrollment.
282
+
283
+ True when the user carries a populated ``totp_secret`` and a
284
+ non-null ``totp_enabled_at`` (from
285
+ :class:`tempest_fastapi_sdk.MFAMixin`). Users without the mixin
286
+ simply never enable MFA.
287
+
288
+ Args:
289
+ principal (Any): A :class:`BaseUserModel` instance.
290
+
291
+ Returns:
292
+ bool: ``True`` to require the TOTP step.
293
+ """
294
+ return bool(
295
+ getattr(principal, "totp_secret", None)
296
+ and getattr(principal, "totp_enabled_at", None)
297
+ )
298
+
299
+ def verify_mfa(self, principal: Any, code: str) -> bool:
300
+ """Verify ``code`` against the user's persisted TOTP secret.
301
+
302
+ Args:
303
+ principal (Any): A :class:`BaseUserModel` instance.
304
+ code (str): The submitted authenticator code.
305
+
306
+ Returns:
307
+ bool: ``True`` when the code is valid.
308
+
309
+ Raises:
310
+ ImportError: When the ``[mfa]`` extra (``pyotp``) is not
311
+ installed but a user has MFA enabled.
312
+ """
313
+ from tempest_fastapi_sdk.utils.totp import TOTPHelper
314
+
315
+ secret = getattr(principal, "totp_secret", None)
316
+ if not secret:
317
+ return False
318
+ return TOTPHelper(issuer=self.mfa_issuer).verify(
319
+ secret, code, window=self.mfa_window
320
+ )
321
+
238
322
 
239
323
  __all__: list[str] = [
240
324
  "AdminAuthBackend",
@@ -72,6 +72,12 @@ class AdminModel(Generic[ModelT]):
72
72
  model name humanized.
73
73
  verbose_name_plural (str | None): Plural display name; defaults to
74
74
  ``verbose_name + "s"``.
75
+ can_create (bool): Whether the admin exposes the create form +
76
+ POST endpoint. Default ``True``.
77
+ can_edit (bool): Whether the admin exposes the edit form + POST
78
+ endpoint. Default ``True``.
79
+ can_delete (bool): Whether the admin exposes the delete action.
80
+ Default ``True``.
75
81
 
76
82
  Raises:
77
83
  TypeError: When ``model`` is not a subclass of :class:`BaseModel`,
@@ -92,6 +98,9 @@ class AdminModel(Generic[ModelT]):
92
98
  repository_class: type[BaseRepository[Any]] | None = None,
93
99
  verbose_name: str | None = None,
94
100
  verbose_name_plural: str | None = None,
101
+ can_create: bool = True,
102
+ can_edit: bool = True,
103
+ can_delete: bool = True,
95
104
  ) -> None:
96
105
  """Build and validate the configuration. See class docstring."""
97
106
  if not isinstance(model, type) or not issubclass(model, BaseModel):
@@ -112,6 +121,9 @@ class AdminModel(Generic[ModelT]):
112
121
  self.repository_class: type[BaseRepository[Any]] | None = repository_class
113
122
  self.verbose_name: str | None = verbose_name
114
123
  self.verbose_name_plural: str | None = verbose_name_plural
124
+ self.can_create: bool = can_create
125
+ self.can_edit: bool = can_edit
126
+ self.can_delete: bool = can_delete
115
127
 
116
128
  def get_verbose_name(self) -> str:
117
129
  """Return the configured (or auto-derived) singular display name.
@@ -165,6 +177,25 @@ class AdminModel(Generic[ModelT]):
165
177
  return list(self.list_display)
166
178
  return [name for name in self.column_names() if name not in {"hashed_password"}]
167
179
 
180
+ def editable_field_names(self) -> list[str]:
181
+ """Return the columns a create/edit form should expose.
182
+
183
+ Excludes the primary key, the audit timestamps
184
+ (``created_at`` / ``updated_at``), the password hash, and any
185
+ column listed in ``readonly_fields`` — none of which a user
186
+ edits directly through the generic admin form.
187
+
188
+ Returns:
189
+ list[str]: Editable column keys in declaration order.
190
+ """
191
+ skip = set(self.readonly_fields) | {
192
+ "id",
193
+ "created_at",
194
+ "updated_at",
195
+ "hashed_password",
196
+ }
197
+ return [name for name in self.column_names() if name not in skip]
198
+
168
199
  def build_repository(self, session: AsyncSession) -> BaseRepository[ModelT]:
169
200
  """Instantiate the repository for ``session``.
170
201