tempest-fastapi-sdk 0.71.0__tar.gz → 0.72.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 (443) hide show
  1. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/CHANGELOG.md +41 -0
  2. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/CLAUDE.md +14 -5
  3. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/PKG-INFO +1 -1
  4. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/admin.en.md +70 -0
  5. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/admin.md +70 -0
  6. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/logging.en.md +9 -0
  7. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/logging.md +9 -0
  8. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/pyproject.toml +1 -1
  9. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/__init__.py +3 -1
  10. tempest_fastapi_sdk-0.72.0/tempest_fastapi_sdk/admin/__init__.py +45 -0
  11. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/site.py +9 -0
  12. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/static/admin.css +12 -2
  13. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/base.html +16 -2
  14. tempest_fastapi_sdk-0.72.0/tempest_fastapi_sdk/admin/theme.py +184 -0
  15. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/Dockerfile.tmpl +8 -0
  16. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/logging.py +62 -18
  17. tempest_fastapi_sdk-0.72.0/tests/admin/test_theme.py +124 -0
  18. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/test_logging.py +43 -0
  19. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/uv.lock +1 -1
  20. tempest_fastapi_sdk-0.71.0/tempest_fastapi_sdk/admin/__init__.py +0 -43
  21. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.github/workflows/ci.yml +0 -0
  22. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.github/workflows/docs.yml +0 -0
  23. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.github/workflows/release-pypi.yml +0 -0
  24. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.gitignore +0 -0
  25. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.python-version +0 -0
  26. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/Makefile +0 -0
  27. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/README.md +0 -0
  28. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/architecture.en.md +0 -0
  29. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/architecture.md +0 -0
  30. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/changelog.en.md +0 -0
  31. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/changelog.md +0 -0
  32. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/contributing.en.md +0 -0
  33. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/contributing.md +0 -0
  34. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/index.en.md +0 -0
  35. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/index.md +0 -0
  36. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/installation.en.md +0 -0
  37. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/installation.md +0 -0
  38. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/index.en.md +0 -0
  39. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/index.md +0 -0
  40. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/api.en.md +0 -0
  41. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/api.md +0 -0
  42. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/business-rules.en.md +0 -0
  43. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/business-rules.md +0 -0
  44. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/domain.en.md +0 -0
  45. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/domain.md +0 -0
  46. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/flows.en.md +0 -0
  47. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/flows.md +0 -0
  48. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/index.en.md +0 -0
  49. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/index.md +0 -0
  50. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/migration.en.md +0 -0
  51. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/migration.md +0 -0
  52. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/audit-trail.en.md +0 -0
  53. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/audit-trail.md +0 -0
  54. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/auth-flow.en.md +0 -0
  55. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/auth-flow.md +0 -0
  56. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/br-helpers.en.md +0 -0
  57. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/br-helpers.md +0 -0
  58. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cache.en.md +0 -0
  59. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cache.md +0 -0
  60. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cli.en.md +0 -0
  61. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cli.md +0 -0
  62. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/database.en.md +0 -0
  63. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/database.md +0 -0
  64. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/deploy-safety.en.md +0 -0
  65. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/deploy-safety.md +0 -0
  66. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/downloads.en.md +0 -0
  67. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/downloads.md +0 -0
  68. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/email.en.md +0 -0
  69. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/email.md +0 -0
  70. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/feature-flags.en.md +0 -0
  71. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/feature-flags.md +0 -0
  72. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http-client.en.md +0 -0
  73. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http-client.md +0 -0
  74. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http.en.md +0 -0
  75. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http.md +0 -0
  76. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/idempotency.en.md +0 -0
  77. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/idempotency.md +0 -0
  78. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/index.en.md +0 -0
  79. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/index.md +0 -0
  80. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/metrics.en.md +0 -0
  81. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/metrics.md +0 -0
  82. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/mfa.en.md +0 -0
  83. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/mfa.md +0 -0
  84. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/multi-tenant.en.md +0 -0
  85. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/multi-tenant.md +0 -0
  86. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/observability.en.md +0 -0
  87. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/observability.md +0 -0
  88. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/offline-sync.en.md +0 -0
  89. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/offline-sync.md +0 -0
  90. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/outbox.en.md +0 -0
  91. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/outbox.md +0 -0
  92. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/queue-tasks.en.md +0 -0
  93. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/queue-tasks.md +0 -0
  94. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/realtime.en.md +0 -0
  95. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/realtime.md +0 -0
  96. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/refresh-tokens.en.md +0 -0
  97. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/refresh-tokens.md +0 -0
  98. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/security.en.md +0 -0
  99. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/security.md +0 -0
  100. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/sessions.en.md +0 -0
  101. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/sessions.md +0 -0
  102. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/storage.en.md +0 -0
  103. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/storage.md +0 -0
  104. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/stored-files.en.md +0 -0
  105. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/stored-files.md +0 -0
  106. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/testing.en.md +0 -0
  107. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/testing.md +0 -0
  108. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/uploads.en.md +0 -0
  109. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/uploads.md +0 -0
  110. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/utilities.en.md +0 -0
  111. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/utilities.md +0 -0
  112. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/webpush.en.md +0 -0
  113. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/webpush.md +0 -0
  114. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/websocket.en.md +0 -0
  115. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/websocket.md +0 -0
  116. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/reference.en.md +0 -0
  117. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/reference.md +0 -0
  118. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/roadmap.en.md +0 -0
  119. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/roadmap.md +0 -0
  120. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/tutorial.en.md +0 -0
  121. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/tutorial.md +0 -0
  122. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/mkdocs.yml +0 -0
  123. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/mkdocs_hooks/llmstxt.py +0 -0
  124. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/scripts/extract_recipe.py +0 -0
  125. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/auth.py +0 -0
  126. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/config.py +0 -0
  127. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/discovery.py +0 -0
  128. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/forms.py +0 -0
  129. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/router.py +0 -0
  130. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/session.py +0 -0
  131. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -0
  132. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/detail.html +0 -0
  133. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/form.html +0 -0
  134. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/list.html +0 -0
  135. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
  136. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/logs.html +0 -0
  137. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/mfa.html +0 -0
  138. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
  139. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
  140. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
  141. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/dependencies/auth.py +0 -0
  142. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
  143. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
  144. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
  145. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
  146. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
  147. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/graceful.py +0 -0
  148. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
  149. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
  150. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
  151. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
  152. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
  153. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
  154. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
  155. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
  156. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
  157. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/server.py +0 -0
  158. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/static.py +0 -0
  159. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/tracing.py +0 -0
  160. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
  161. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
  162. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/guards.py +0 -0
  163. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/locale.py +0 -0
  164. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
  165. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/router.py +0 -0
  166. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
  167. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/service.py +0 -0
  168. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/activation.html +0 -0
  169. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_error.html +0 -0
  170. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_success.html +0 -0
  171. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset.html +0 -0
  172. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_error.html +0 -0
  173. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_form.html +0 -0
  174. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset_success.html +0 -0
  175. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation.html +0 -0
  176. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_error.html +0 -0
  177. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_success.html +0 -0
  178. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset.html +0 -0
  179. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_error.html +0 -0
  180. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_form.html +0 -0
  181. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset_success.html +0 -0
  182. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
  183. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
  184. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/invalidation.py +0 -0
  185. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
  186. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
  187. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
  188. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/dockerignore.tmpl +0 -0
  189. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +0 -0
  190. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
  191. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
  192. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
  193. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
  194. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
  195. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +0 -0
  196. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/__init__.py.tmpl +0 -0
  197. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/auth.py.tmpl +0 -0
  198. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/dependencies/resources.py.tmpl +0 -0
  199. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/routers/__init__.py.tmpl +0 -0
  200. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
  201. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
  202. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
  203. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -0
  204. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
  205. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/__init__.py.tmpl +0 -0
  206. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/db/models/user.py.tmpl +0 -0
  207. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/db/repositories/__init__.py.tmpl +0 -0
  208. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
  209. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
  210. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
  211. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
  212. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
  213. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
  214. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/db.py +0 -0
  215. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
  216. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
  217. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
  218. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/main.py +0 -0
  219. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/new.py +0 -0
  220. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/secrets.py +0 -0
  221. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/src_layers.py +0 -0
  222. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/user.py +0 -0
  223. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
  224. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
  225. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
  226. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/context.py +0 -0
  227. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/enums.py +0 -0
  228. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/__init__.py +0 -0
  229. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
  230. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
  231. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
  232. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/audit.py +0 -0
  233. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/backup.py +0 -0
  234. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/connection.py +0 -0
  235. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
  236. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
  237. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/model.py +0 -0
  238. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/outbox.py +0 -0
  239. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/repository.py +0 -0
  240. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/slow_query.py +0 -0
  241. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/tenant.py +0 -0
  242. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
  243. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
  244. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_refresh_token_model.py +0 -0
  245. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
  246. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
  247. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
  248. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
  249. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
  250. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/i18n.py +0 -0
  251. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
  252. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
  253. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
  254. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
  255. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
  256. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
  257. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/__init__.py +0 -0
  258. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/backends.py +0 -0
  259. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/dependencies.py +0 -0
  260. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/service.py +0 -0
  261. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/py.typed +0 -0
  262. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
  263. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
  264. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
  265. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
  266. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
  267. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
  268. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/pagination.py +0 -0
  269. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
  270. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
  271. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/services/base.py +0 -0
  272. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/services/file_mixin.py +0 -0
  273. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
  274. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
  275. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
  276. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
  277. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
  278. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
  279. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
  280. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
  281. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/settings/base.py +0 -0
  282. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
  283. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sse/__init__.py +0 -0
  284. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
  285. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
  286. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
  287. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
  288. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
  289. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
  290. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
  291. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/testing/database.py +0 -0
  292. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
  293. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
  294. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/data/br_locations.json +0 -0
  295. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
  296. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
  297. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/download.py +0 -0
  298. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/email.py +0 -0
  299. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
  300. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
  301. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/locations.py +0 -0
  302. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/log.py +0 -0
  303. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
  304. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
  305. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/password.py +0 -0
  306. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
  307. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
  308. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
  309. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
  310. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
  311. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
  312. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
  313. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
  314. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
  315. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
  316. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
  317. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
  318. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/__init__.py +0 -0
  319. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/__init__.py +0 -0
  320. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_auth.py +0 -0
  321. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_discovery.py +0 -0
  322. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_forms.py +0 -0
  323. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_logs_nav.py +0 -0
  324. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_mfa.py +0 -0
  325. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_router.py +0 -0
  326. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_site.py +0 -0
  327. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_user_model.py +0 -0
  328. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/__init__.py +0 -0
  329. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_body_size.py +0 -0
  330. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_cookies.py +0 -0
  331. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_cors.py +0 -0
  332. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_csrf.py +0 -0
  333. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_dependencies_auth.py +0 -0
  334. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_graceful.py +0 -0
  335. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_handlers.py +0 -0
  336. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_health_router.py +0 -0
  337. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_idempotency.py +0 -0
  338. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_jwt_dependency.py +0 -0
  339. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_logs_router.py +0 -0
  340. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_oauth.py +0 -0
  341. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_prometheus.py +0 -0
  342. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_rate_limit.py +0 -0
  343. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_rate_limit_extras.py +0 -0
  344. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_request_id_middleware.py +0 -0
  345. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_role_dependency.py +0 -0
  346. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_server.py +0 -0
  347. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_static.py +0 -0
  348. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_tool_spec.py +0 -0
  349. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_tracing.py +0 -0
  350. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_webhooks.py +0 -0
  351. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_webhooks_rsa.py +0 -0
  352. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/__init__.py +0 -0
  353. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_guards.py +0 -0
  354. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_locale.py +0 -0
  355. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_mfa.py +0 -0
  356. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_refresh.py +0 -0
  357. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_refresh_db.py +0 -0
  358. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_service.py +0 -0
  359. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/__init__.py +0 -0
  360. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/test_decorator.py +0 -0
  361. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/test_invalidation.py +0 -0
  362. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/test_redis_manager.py +0 -0
  363. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/__init__.py +0 -0
  364. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_db.py +0 -0
  365. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_db_seed.py +0 -0
  366. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_docker_compose.py +0 -0
  367. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_generate.py +0 -0
  368. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_main.py +0 -0
  369. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_secrets.py +0 -0
  370. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_user.py +0 -0
  371. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/conftest.py +0 -0
  372. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/controllers/__init__.py +0 -0
  373. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/controllers/test_base.py +0 -0
  374. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/__init__.py +0 -0
  375. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/test_context.py +0 -0
  376. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/test_enums.py +0 -0
  377. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/__init__.py +0 -0
  378. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_alembic_hooks.py +0 -0
  379. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_audit.py +0 -0
  380. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_backup.py +0 -0
  381. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_bulk_ops.py +0 -0
  382. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_connection.py +0 -0
  383. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_migrations.py +0 -0
  384. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_mixins.py +0 -0
  385. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_model.py +0 -0
  386. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_outbox.py +0 -0
  387. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_repository.py +0 -0
  388. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_safe_upgrade.py +0 -0
  389. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_slow_query.py +0 -0
  390. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_tenant.py +0 -0
  391. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/exceptions/__init__.py +0 -0
  392. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/exceptions/test_exceptions.py +0 -0
  393. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/exceptions/test_i18n.py +0 -0
  394. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/flags/__init__.py +0 -0
  395. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/flags/test_flags.py +0 -0
  396. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/queue/__init__.py +0 -0
  397. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/queue/test_manager.py +0 -0
  398. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/__init__.py +0 -0
  399. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_base.py +0 -0
  400. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_cursor_pagination.py +0 -0
  401. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_link_headers.py +0 -0
  402. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_pagination.py +0 -0
  403. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_response.py +0 -0
  404. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/services/__init__.py +0 -0
  405. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/services/test_base.py +0 -0
  406. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/services/test_file_mixin.py +0 -0
  407. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sessions/__init__.py +0 -0
  408. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sessions/test_sessions.py +0 -0
  409. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/settings/__init__.py +0 -0
  410. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/settings/test_base.py +0 -0
  411. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/settings/test_mixins.py +0 -0
  412. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sse/__init__.py +0 -0
  413. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sse/test_event_stream.py +0 -0
  414. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/storage/__init__.py +0 -0
  415. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/storage/test_minio_client.py +0 -0
  416. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/tasks/__init__.py +0 -0
  417. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/tasks/test_manager.py +0 -0
  418. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/tasks/test_scheduler.py +0 -0
  419. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/testing/__init__.py +0 -0
  420. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/testing/test_database.py +0 -0
  421. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/__init__.py +0 -0
  422. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_client_ip.py +0 -0
  423. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_datetime.py +0 -0
  424. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_dict.py +0 -0
  425. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_download.py +0 -0
  426. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_email.py +0 -0
  427. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_http_client.py +0 -0
  428. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_jwt.py +0 -0
  429. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_lazy_extras.py +0 -0
  430. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_locations.py +0 -0
  431. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_log.py +0 -0
  432. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_metrics.py +0 -0
  433. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_opaque_token.py +0 -0
  434. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_password.py +0 -0
  435. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_regex.py +0 -0
  436. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_storage_backends.py +0 -0
  437. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_throttle.py +0 -0
  438. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_upload.py +0 -0
  439. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/webpush/__init__.py +0 -0
  440. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/webpush/test_dispatcher.py +0 -0
  441. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/webpush/test_schemas.py +0 -0
  442. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/websockets/__init__.py +0 -0
  443. {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/websockets/test_hub_and_router.py +0 -0
@@ -5,6 +5,47 @@ 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.72.0] — 2026-06-26
9
+
10
+ ### Added
11
+
12
+ - **`AdminTheme` — typed theming for the admin panel.** A new
13
+ `AdminTheme` dataclass carries appearance overrides (accent /
14
+ accent_hover / danger colors, header & sidebar backgrounds, page
15
+ background, border radius, font family, logo image + alt, favicon,
16
+ footer text, dark mode, and a `custom_css_url` escape hatch) through
17
+ typed, documented parameters. Pass it via `AdminSite(theme=...)`; the
18
+ SDK injects a `<style>` block of `:root` overrides after `admin.css`
19
+ (so it wins) plus the favicon / logo / footer chrome. `AdminTheme()`
20
+ is a no-op that reproduces the stock look, so existing sites are
21
+ unchanged. String fields reject `< > { } "` at construction to keep a
22
+ value from breaking the injected markup. Exported from
23
+ `tempest_fastapi_sdk` and `tempest_fastapi_sdk.admin`.
24
+
25
+ ## [0.71.1] — 2026-06-26
26
+
27
+ ### Fixed
28
+
29
+ - **File logging no longer crashes the app on a non-writable filesystem.**
30
+ `configure_logging` now treats file logging as best-effort: if `log_dir`
31
+ cannot be created or its files cannot be opened (read-only mount, missing
32
+ write permission, hardened container, serverless, CI), the file handlers
33
+ are skipped, a warning is emitted (to the logger when stdout is on, else
34
+ straight to `stderr`), and the service keeps running with stdout logging
35
+ instead of dying at import time with
36
+ `PermissionError: [Errno 13] ... 'logs'`. `_build_file_handlers` also
37
+ closes any handlers it opened before a mid-build failure so no file
38
+ descriptors leak.
39
+ - **Scaffold `Dockerfile` fixed so the non-root `app` user can write
40
+ `logs/`.** `WORKDIR /app` created `/app` as `root` before the
41
+ `COPY --chown=app:app`, and `--chown` only sets ownership on the copied
42
+ *contents* — not on the pre-existing `/app` directory node — so the `app`
43
+ user could not create `logs/` (or the SQLite `app.db`) inside it and the
44
+ container crash-looped at startup. The template now runs
45
+ `RUN mkdir -p /app/logs && chown -R app:app /app` after the copy. Existing
46
+ projects: regenerate with `tempest generate --dockerfile --force` or add
47
+ that line by hand.
48
+
8
49
  ## [0.71.0] — 2026-06-26
9
50
 
10
51
  ### Added
@@ -129,7 +129,9 @@ The SDK currently covers (Sep 2025+, post-v0.31.x):
129
129
  (`audit_model=...` + `add_audited` / `update_audited` /
130
130
  `delete_audited`, same-tx).
131
131
  - **Admin panel** — Jinja + HTMX (`AdminSite`, `AdminModel`,
132
- `make_admin_router`).
132
+ `make_admin_router`), typed theming via `AdminTheme` (colors /
133
+ logo / favicon / font / radius / footer / dark mode /
134
+ `custom_css_url`, injected as `:root` overrides).
133
135
  - **CLI** — `tempest new` (scaffolds layered service +
134
136
  docker-compose + multi-stage uv `Dockerfile`/`.dockerignore`),
135
137
  `tempest generate --docker` (regen compose) / `--dockerfile`
@@ -160,12 +162,19 @@ mostly wiring, not greenfield.
160
162
  Build in tiers. Ship each item, document it (same-commit docs rule),
161
163
  then move it up to the covers list.
162
164
 
165
+ **Shipped — `AdminTheme` (v0.72.0).** Typed appearance overrides
166
+ (colors / logo / favicon / font / radius / footer / dark mode /
167
+ `custom_css_url`) injected as `:root` CSS-variable overrides via
168
+ `AdminSite(theme=...)`. This is the "beautiful + typed customization"
169
+ foundation the user asked for first; the functional Tier 1 items below
170
+ inherit the look for free. Now in the covers list.
171
+
163
172
  **Tier 1 — high value, reuses an existing engine (low effort):**
164
173
 
165
- 1. **Custom actions** — `@admin_action` decorator on `AdminModel` for
166
- user-defined row / bulk operations (Django `actions`, Nova actions).
167
- Today only 3 are hardcoded (activate / deactivate / delete). This is
168
- the foundation the rest build on.
174
+ 1. **Custom actions (NEXT)** — `@admin_action` decorator on `AdminModel`
175
+ for user-defined row / bulk operations (Django `actions`, Nova
176
+ actions). Today only 3 are hardcoded (activate / deactivate /
177
+ delete). This is the foundation the rest build on.
169
178
  2. **File / image upload field** — a new form widget wired to the
170
179
  existing `UploadUtils` (`LocalUploadStorage` / `MinIOUploadStorage`).
171
180
  Engine is already shipped; admin just needs the widget + storage
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tempest-fastapi-sdk
3
- Version: 0.71.0
3
+ Version: 0.72.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
@@ -271,3 +271,73 @@ class OAuthAdminBackend(AdminAuthBackend):
271
271
 
272
272
  Pass the instance via `auth_backend=` and the rest of the admin pipeline (sessions, dashboard, list, detail) keeps working unchanged.
273
273
 
274
+ #### 6. Customize the look — `AdminTheme`
275
+
276
+ The admin CSS is driven entirely by **CSS custom properties** on `:root`. Instead of forking the stylesheet, you pass an `AdminTheme` with **typed, documented parameters** — colors, logo, favicon, font, radius, footer, dark mode — and the SDK injects a `<style>` block in the `<head>` (after `admin.css`, so it wins).
277
+
278
+ ```python
279
+ # src/admin/site.py
280
+ from tempest_fastapi_sdk import AdminSite, AdminTheme
281
+
282
+ theme: AdminTheme = AdminTheme(
283
+ accent="#7c3aed", # primary color (links, buttons, active item)
284
+ accent_hover="#6d28d9", # hover shade of the accent
285
+ header_bg="#1e1b4b", # header/sidebar background
286
+ radius="10px", # radius of buttons, inputs, cards, tables
287
+ font_family="'Inter', system-ui, sans-serif",
288
+ logo_url="/admin/static/logo.svg", # header image (instead of the text brand)
289
+ favicon_url="/admin/static/favicon.ico",
290
+ footer_text="Servus | 2026",
291
+ dark_mode=False, # dark content surfaces
292
+ )
293
+
294
+ site: AdminSite = AdminSite(title="Servus Admin", brand="Servus", theme=theme)
295
+ ```
296
+
297
+ `AdminTheme()` with no arguments is a **no-op**: it reproduces the stock look. You only set what you want to change.
298
+
299
+ !!! tip "The golden rule"
300
+ Every `AdminTheme` field maps to a `:root` CSS variable (or to a piece
301
+ of chrome, like the logo). It is all typed — the editor autocompletes
302
+ the options and mypy validates — and no string ever needs to be a CSS
303
+ class name or selector.
304
+
305
+ | Field | Type | Default | Effect |
306
+ |-------|------|---------|--------|
307
+ | `accent` | `str` | `"#2563eb"` | Primary color: links, buttons, active sidebar item |
308
+ | `accent_hover` | `str` | `"#1d4ed8"` | Hover/active shade of `accent` |
309
+ | `danger` | `str` | `"#b91c1c"` | Destructive actions and error messages |
310
+ | `header_bg` | `str` | `"#0f172a"` | Header background |
311
+ | `sidebar_bg` | `str \| None` | `None` | Sidebar background (falls back to `header_bg`) |
312
+ | `page_bg` | `str \| None` | `None` | Content background (mode default) |
313
+ | `radius` | `str` | `"6px"` | Radius of buttons, inputs, cards, tables |
314
+ | `font_family` | `str \| None` | `None` | `font-family` for the whole panel |
315
+ | `logo_url` | `str \| None` | `None` | Header image instead of the text brand |
316
+ | `logo_alt` | `str` | `"Logo"` | `alt` text for the logo image |
317
+ | `favicon_url` | `str \| None` | `None` | Browser-tab favicon |
318
+ | `footer_text` | `str` | `"Powered by tempest-fastapi-sdk"` | Footer text |
319
+ | `dark_mode` | `bool` | `False` | Dark content surfaces |
320
+ | `custom_css_url` | `str \| None` | `None` | Extra stylesheet, linked last |
321
+
322
+ !!! info "Dark mode"
323
+ `dark_mode=True` switches the **content surfaces** (page background,
324
+ text, table rows, inputs, borders) to a dark palette. The
325
+ header/sidebar are already dark, so they are unaffected; `accent` and
326
+ the other colors still apply. An explicit `page_bg` wins over dark mode.
327
+
328
+ !!! warning "Escape hatch for the rest"
329
+ For anything the fields do not cover, point `custom_css_url` at your own
330
+ stylesheet. It is linked **after** the theme, so it overrides
331
+ everything — including `AdminTheme`.
332
+
333
+ !!! danger "Values are developer-set, not end-user input"
334
+ The characters `< > { } "` are rejected in any string field
335
+ (`ValueError` at construction), because they would break the injected
336
+ `<style>` block or an HTML attribute. Never derive `AdminTheme` values
337
+ from end-user input.
338
+
339
+ **Recap:** instantiate `AdminTheme` with the fields you want to change,
340
+ pass it via `AdminSite(theme=...)`, and the look changes across every page
341
+ (login, dashboard, list, detail, forms) without touching CSS. For full
342
+ control, `custom_css_url`.
343
+
@@ -252,3 +252,73 @@ class OAuthAdminBackend(AdminAuthBackend):
252
252
  ```
253
253
 
254
254
  Passe a instância via `auth_backend=` e o resto do pipeline do admin (sessões, dashboard, list, detail) segue funcionando sem mudanças.
255
+
256
+ #### 6. Customizar a aparência — `AdminTheme`
257
+
258
+ O CSS do admin é todo dirigido por **CSS custom properties** em `:root`. Em vez de forkar a folha de estilo, você passa um `AdminTheme` com **parâmetros tipados e documentados** — cores, logo, favicon, fonte, raio, rodapé, modo escuro — e a SDK injeta um bloco `<style>` no `<head>` (depois do `admin.css`, então ele vence).
259
+
260
+ ```python
261
+ # src/admin/site.py
262
+ from tempest_fastapi_sdk import AdminSite, AdminTheme
263
+
264
+ theme: AdminTheme = AdminTheme(
265
+ accent="#7c3aed", # cor primária (links, botões, item ativo)
266
+ accent_hover="#6d28d9", # tom de hover do accent
267
+ header_bg="#1e1b4b", # fundo do header/sidebar
268
+ radius="10px", # raio de botões, inputs, cards, tabelas
269
+ font_family="'Inter', system-ui, sans-serif",
270
+ logo_url="/admin/static/logo.svg", # imagem no header (no lugar do texto)
271
+ favicon_url="/admin/static/favicon.ico",
272
+ footer_text="Servus | 2026",
273
+ dark_mode=False, # superfícies de conteúdo escuras
274
+ )
275
+
276
+ site: AdminSite = AdminSite(title="Servus Admin", brand="Servus", theme=theme)
277
+ ```
278
+
279
+ `AdminTheme()` sem argumentos é um **no-op**: reproduz a aparência padrão. Você só define o que quer mudar.
280
+
281
+ !!! tip "A regra de ouro"
282
+ Cada campo do `AdminTheme` mapeia para uma variável CSS de `:root` (ou
283
+ para um pedaço de chrome, como o logo). É tudo tipado — o autocomplete
284
+ do editor lista as opções e o mypy valida — e nenhuma string precisa
285
+ ser um nome de classe CSS ou seletor.
286
+
287
+ | Campo | Tipo | Padrão | Efeito |
288
+ |-------|------|--------|--------|
289
+ | `accent` | `str` | `"#2563eb"` | Cor primária: links, botões, item ativo da sidebar |
290
+ | `accent_hover` | `str` | `"#1d4ed8"` | Tom de hover/ativo do `accent` |
291
+ | `danger` | `str` | `"#b91c1c"` | Ações destrutivas e mensagens de erro |
292
+ | `header_bg` | `str` | `"#0f172a"` | Fundo do header |
293
+ | `sidebar_bg` | `str \| None` | `None` | Fundo da sidebar (cai pra `header_bg`) |
294
+ | `page_bg` | `str \| None` | `None` | Fundo do conteúdo (padrão do modo) |
295
+ | `radius` | `str` | `"6px"` | Raio de botões, inputs, cards, tabelas |
296
+ | `font_family` | `str \| None` | `None` | `font-family` do painel inteiro |
297
+ | `logo_url` | `str \| None` | `None` | Imagem no header em vez do texto |
298
+ | `logo_alt` | `str` | `"Logo"` | `alt` da imagem do logo |
299
+ | `favicon_url` | `str \| None` | `None` | Favicon da aba |
300
+ | `footer_text` | `str` | `"Powered by tempest-fastapi-sdk"` | Texto do rodapé |
301
+ | `dark_mode` | `bool` | `False` | Superfícies de conteúdo escuras |
302
+ | `custom_css_url` | `str \| None` | `None` | Folha de estilo extra, linkada por último |
303
+
304
+ !!! info "Modo escuro"
305
+ `dark_mode=True` troca as **superfícies de conteúdo** (fundo da página,
306
+ texto, linhas da tabela, inputs, bordas) para uma paleta escura. O
307
+ header/sidebar já são escuros, então não mudam; `accent` e as outras
308
+ cores continuam valendo. Um `page_bg` explícito vence o modo escuro.
309
+
310
+ !!! warning "Escape hatch para o resto"
311
+ Para o que os campos não cobrem, aponte `custom_css_url` para a sua
312
+ própria folha de estilo. Ela é linkada **depois** do tema, então
313
+ sobrescreve tudo — inclusive o `AdminTheme`.
314
+
315
+ !!! danger "Valores são do desenvolvedor, não do usuário final"
316
+ Os caracteres `< > { } "` são rejeitados em qualquer campo de texto
317
+ (`ValueError` na construção), porque quebrariam o `<style>` injetado ou
318
+ um atributo HTML. Nunca derive valores de `AdminTheme` de entrada de
319
+ usuário final.
320
+
321
+ **Recap:** instancie `AdminTheme` com os campos que quer mudar, passe via
322
+ `AdminSite(theme=...)`, e a aparência muda em todas as páginas (login,
323
+ dashboard, list, detail, forms) sem tocar em CSS. Para customização total,
324
+ `custom_css_url`.
@@ -66,6 +66,15 @@ configure_logging(level="INFO", stdout=False)
66
66
  `ValueError` — silencing every handler leaves the application
67
67
  blind.
68
68
 
69
+ !!! check "File logging is best-effort — it never crashes startup"
70
+ If `log_dir` cannot be created or its files cannot be opened
71
+ (read-only filesystem, missing write permission, hardened container,
72
+ serverless, CI), the SDK **skips** the file handlers, emits a warning
73
+ (to the logger when stdout is on, otherwise straight to `stderr`) and
74
+ keeps running with stdout only — instead of dying at import with
75
+ `PermissionError: [Errno 13] ... 'logs'`. Pass `file_output=False` to
76
+ opt out of file logging explicitly.
77
+
69
78
  On disk:
70
79
 
71
80
  ```text
@@ -66,6 +66,15 @@ configure_logging(level="INFO", stdout=False)
66
66
  `ValueError` — silenciar todos os handlers deixa a aplicação
67
67
  cega.
68
68
 
69
+ !!! check "Log em arquivo é best-effort — nunca derruba o boot"
70
+ Se o `log_dir` não puder ser criado ou seus arquivos não puderem ser
71
+ abertos (FS read-only, falta de permissão de escrita, container
72
+ endurecido, serverless, CI), o SDK **pula** os handlers de arquivo,
73
+ emite um aviso (no logger quando o stdout está ligado, senão direto no
74
+ `stderr`) e segue rodando só com stdout — em vez de morrer no import com
75
+ `PermissionError: [Errno 13] ... 'logs'`. Para abrir mão do log em
76
+ arquivo de forma explícita, passe `file_output=False`.
77
+
69
78
  O resultado em disco:
70
79
 
71
80
  ```text
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tempest-fastapi-sdk"
3
- version = "0.71.0"
3
+ version = "0.72.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"
@@ -5,6 +5,7 @@ from tempest_fastapi_sdk.admin import (
5
5
  AdminAuthError,
6
6
  AdminModel,
7
7
  AdminSite,
8
+ AdminTheme,
8
9
  FieldRef,
9
10
  OrderRef,
10
11
  UserModelAuthBackend,
@@ -315,7 +316,7 @@ from tempest_fastapi_sdk.websockets import (
315
316
  make_websocket_router,
316
317
  )
317
318
 
318
- __version__: str = "0.71.0"
319
+ __version__: str = "0.72.0"
319
320
 
320
321
  __all__: list[str] = [
321
322
  "BASE_COLUMN_ORDER",
@@ -344,6 +345,7 @@ __all__: list[str] = [
344
345
  "AdminAuthError",
345
346
  "AdminModel",
346
347
  "AdminSite",
348
+ "AdminTheme",
347
349
  "AlembicHelper",
348
350
  "AppException",
349
351
  "AsyncDatabaseManager",
@@ -0,0 +1,45 @@
1
+ """Django-admin-style management UI for FastAPI services.
2
+
3
+ Mount via :func:`make_admin_router`; register one :class:`AdminModel`
4
+ per SQLAlchemy model on the :class:`AdminSite` instance. The site
5
+ auto-derives list/detail views from the existing
6
+ :class:`tempest_fastapi_sdk.BaseRepository` so no extra controllers
7
+ are needed for read-only management.
8
+
9
+ Requires the ``[admin]`` optional extra (``jinja2`` +
10
+ ``itsdangerous``). Authentication relies on
11
+ :class:`tempest_fastapi_sdk.BaseUserModel`: any user with
12
+ ``is_admin=True`` may sign in.
13
+ """
14
+
15
+ from tempest_fastapi_sdk.admin.auth import AdminAuthBackend as AdminAuthBackend
16
+ from tempest_fastapi_sdk.admin.auth import AdminAuthError as AdminAuthError
17
+ from tempest_fastapi_sdk.admin.auth import UserModelAuthBackend as UserModelAuthBackend
18
+ from tempest_fastapi_sdk.admin.config import AdminModel as AdminModel
19
+ from tempest_fastapi_sdk.admin.config import FieldRef as FieldRef
20
+ from tempest_fastapi_sdk.admin.config import OrderRef as OrderRef
21
+ from tempest_fastapi_sdk.admin.discovery import discover_models as discover_models
22
+ from tempest_fastapi_sdk.admin.router import make_admin_router as make_admin_router
23
+ from tempest_fastapi_sdk.admin.session import AdminSession as AdminSession
24
+ from tempest_fastapi_sdk.admin.session import SessionStore as SessionStore
25
+ from tempest_fastapi_sdk.admin.session import (
26
+ SignedCookieSessionStore as SignedCookieSessionStore,
27
+ )
28
+ from tempest_fastapi_sdk.admin.site import AdminSite as AdminSite
29
+ from tempest_fastapi_sdk.admin.theme import AdminTheme as AdminTheme
30
+
31
+ __all__: list[str] = [
32
+ "AdminAuthBackend",
33
+ "AdminAuthError",
34
+ "AdminModel",
35
+ "AdminSession",
36
+ "AdminSite",
37
+ "AdminTheme",
38
+ "FieldRef",
39
+ "OrderRef",
40
+ "SessionStore",
41
+ "SignedCookieSessionStore",
42
+ "UserModelAuthBackend",
43
+ "discover_models",
44
+ "make_admin_router",
45
+ ]
@@ -6,6 +6,7 @@ from types import ModuleType
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from tempest_fastapi_sdk.admin.config import AdminModel
9
+ from tempest_fastapi_sdk.admin.theme import AdminTheme
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  from collections.abc import Sequence
@@ -30,6 +31,9 @@ class AdminSite:
30
31
  index_subtitle (str): Optional subtitle for the dashboard.
31
32
  site_url (str | None): Optional "View site" link rendered in
32
33
  the admin header.
34
+ theme (AdminTheme): Typed appearance overrides (colors, logo,
35
+ favicon, font, footer, dark mode). Defaults to a stock
36
+ :class:`AdminTheme`, so the look is unchanged when omitted.
33
37
  """
34
38
 
35
39
  def __init__(
@@ -39,6 +43,7 @@ class AdminSite:
39
43
  brand: str | None = None,
40
44
  index_subtitle: str = "Site administration",
41
45
  site_url: str | None = None,
46
+ theme: AdminTheme | None = None,
42
47
  ) -> None:
43
48
  """Initialize the site.
44
49
 
@@ -53,11 +58,15 @@ class AdminSite:
53
58
  index_subtitle (str): Dashboard subtitle.
54
59
  site_url (str | None): Optional outbound link rendered
55
60
  in the admin header.
61
+ theme (AdminTheme | None): Typed appearance overrides.
62
+ ``None`` (default) uses a stock :class:`AdminTheme`,
63
+ leaving the look identical to earlier versions.
56
64
  """
57
65
  self.title: str = title
58
66
  self.brand: str | None = brand
59
67
  self.index_subtitle: str = index_subtitle
60
68
  self.site_url: str | None = site_url
69
+ self.theme: AdminTheme = theme or AdminTheme()
61
70
  self._registry: dict[str, AdminModel[Any]] = {}
62
71
 
63
72
  @property
@@ -18,10 +18,10 @@
18
18
 
19
19
  body {
20
20
  margin: 0;
21
- font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
21
+ font-family: var(--tempest-font, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
22
22
  font-size: 14px;
23
23
  color: var(--tempest-fg);
24
- background: #f8fafc;
24
+ background: var(--tempest-page-bg, #f8fafc);
25
25
  /* Sticky-footer column so the layout (and its sidebar) fills the
26
26
  viewport even when the page content is short. */
27
27
  min-height: 100vh;
@@ -90,6 +90,16 @@ textarea {
90
90
  font-weight: 600;
91
91
  font-size: 1.1rem;
92
92
  pointer-events: auto;
93
+ display: inline-flex;
94
+ align-items: center;
95
+ }
96
+
97
+ /* Logo image (shown instead of the brand text when theme.logo_url is
98
+ set). Capped to the header height so any aspect ratio fits cleanly. */
99
+ .tempest-admin-header__logo {
100
+ display: block;
101
+ max-height: 32px;
102
+ width: auto;
93
103
  }
94
104
 
95
105
  .tempest-admin-header__nav {
@@ -4,7 +4,14 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>{% block title %}{{ site.title }}{% endblock %}</title>
7
+ {% if site.theme.favicon_url %}
8
+ <link rel="icon" href="{{ site.theme.favicon_url }}">
9
+ {% endif %}
7
10
  <link rel="stylesheet" href="{{ static_url }}/admin.css">
11
+ <style>{{ site.theme.to_css() }}</style>
12
+ {% if site.theme.custom_css_url %}
13
+ <link rel="stylesheet" href="{{ site.theme.custom_css_url }}">
14
+ {% endif %}
8
15
  <script src="https://unpkg.com/htmx.org@2.0.3" defer></script>
9
16
  </head>
10
17
  <body>
@@ -19,7 +26,14 @@
19
26
  </label>
20
27
  {% endif %}
21
28
  <div class="tempest-admin-header__brand">
22
- <a href="{{ url_for('admin_index') }}">{{ site.brand_text }}</a>
29
+ <a href="{{ url_for('admin_index') }}">
30
+ {% if site.theme.logo_url %}
31
+ <img src="{{ site.theme.logo_url }}" alt="{{ site.theme.logo_alt }}"
32
+ class="tempest-admin-header__logo">
33
+ {% else %}
34
+ {{ site.brand_text }}
35
+ {% endif %}
36
+ </a>
23
37
  </div>
24
38
  </div>
25
39
  {% if user %}
@@ -76,7 +90,7 @@
76
90
  </main>
77
91
  </div>
78
92
  <footer class="tempest-admin-footer">
79
- <small>Powered by tempest-fastapi-sdk</small>
93
+ <small>{{ site.theme.footer_text }}</small>
80
94
  </footer>
81
95
  </body>
82
96
  </html>
@@ -0,0 +1,184 @@
1
+ """Typed theming for the admin panel.
2
+
3
+ The bundled stylesheet (``static/admin.css``) is driven entirely by CSS
4
+ custom properties declared on ``:root``. :class:`AdminTheme` lets a
5
+ project override those properties — plus the logo, favicon, font and
6
+ footer — through **typed, documented parameters** instead of forking the
7
+ stylesheet. The values are injected as a ``<style>`` block in the page
8
+ ``<head>`` (after ``admin.css``, so they win), which means:
9
+
10
+ * no CSS file to maintain on the project side,
11
+ * every knob is type-checked and discoverable in the IDE,
12
+ * the default look is unchanged when no theme is passed.
13
+
14
+ Example::
15
+
16
+ from tempest_fastapi_sdk.admin import AdminSite, AdminTheme
17
+
18
+ site = AdminSite(
19
+ title="Servus Admin",
20
+ theme=AdminTheme(
21
+ accent="#7c3aed",
22
+ header_bg="#1e1b4b",
23
+ logo_url="/static/logo.svg",
24
+ font_family="'Inter', sans-serif",
25
+ radius="10px",
26
+ footer_text="Servus | 2026",
27
+ ),
28
+ )
29
+
30
+ For anything the parameters do not cover, point :attr:`custom_css_url`
31
+ at your own stylesheet — it is linked last, so it overrides everything,
32
+ including this theme.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ from dataclasses import dataclass, fields
38
+
39
+ # Characters that would let a theme value break out of the injected
40
+ # ``<style>`` block or an HTML attribute. Theme values are developer-set
41
+ # (not end-user input), but rejecting these keeps a careless value from
42
+ # silently producing broken — or injectable — markup.
43
+ _FORBIDDEN_CHARS: tuple[str, ...] = ("<", ">", "{", "}", '"')
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class AdminTheme:
48
+ """Typed appearance overrides for the admin panel.
49
+
50
+ Every field maps to a CSS custom property on ``:root`` (or to a
51
+ piece of chrome like the logo / footer). All fields have sensible
52
+ defaults that reproduce the stock look, so ``AdminTheme()`` is a
53
+ no-op and you only set what you want to change.
54
+
55
+ Attributes:
56
+ accent (str): Primary accent color — links, primary buttons,
57
+ active sidebar item. Any CSS color (``"#7c3aed"``,
58
+ ``"rebeccapurple"``, ``"rgb(124 58 237)"``).
59
+ accent_hover (str): Hover/active shade of :attr:`accent`.
60
+ danger (str): Color for destructive actions and error messages.
61
+ header_bg (str): Background of the top header band.
62
+ sidebar_bg (str | None): Background of the left sidebar. Falls
63
+ back to :attr:`header_bg` when ``None`` so the chrome reads
64
+ as one surface.
65
+ page_bg (str | None): Main content background. ``None`` uses the
66
+ mode default (light grey, or near-black when
67
+ :attr:`dark_mode` is on).
68
+ radius (str): Border-radius applied to buttons, inputs, cards
69
+ and tables (``"6px"``, ``"0.75rem"``, ``"0"`` for square).
70
+ font_family (str | None): CSS ``font-family`` for the whole
71
+ panel. ``None`` keeps the system font stack.
72
+ logo_url (str | None): URL of an image shown in the header
73
+ instead of the brand text. ``None`` shows the text brand.
74
+ logo_alt (str): ``alt`` text for the logo image.
75
+ favicon_url (str | None): URL of the browser-tab favicon.
76
+ ``None`` lets the browser use its default.
77
+ footer_text (str): Text shown in the page footer.
78
+ dark_mode (bool): When ``True``, the content surfaces (page
79
+ background, text, table rows, inputs, borders) switch to a
80
+ dark palette. The header/sidebar are already dark, so they
81
+ are unaffected; :attr:`accent` and the other colors still
82
+ apply.
83
+ custom_css_url (str | None): URL of an extra stylesheet linked
84
+ **after** the theme, so it overrides everything. The escape
85
+ hatch for anything the typed fields do not cover.
86
+ """
87
+
88
+ accent: str = "#2563eb"
89
+ accent_hover: str = "#1d4ed8"
90
+ danger: str = "#b91c1c"
91
+ header_bg: str = "#0f172a"
92
+ sidebar_bg: str | None = None
93
+ page_bg: str | None = None
94
+ radius: str = "6px"
95
+ font_family: str | None = None
96
+ logo_url: str | None = None
97
+ logo_alt: str = "Logo"
98
+ favicon_url: str | None = None
99
+ footer_text: str = "Powered by tempest-fastapi-sdk"
100
+ dark_mode: bool = False
101
+ custom_css_url: str | None = None
102
+
103
+ def __post_init__(self) -> None:
104
+ """Validate that no string field can break out of the markup.
105
+
106
+ Raises:
107
+ ValueError: When any string field contains a character from
108
+ :data:`_FORBIDDEN_CHARS` (``< > { } "``), which would
109
+ corrupt the injected ``<style>`` block or an HTML
110
+ attribute.
111
+ """
112
+ for field in fields(self):
113
+ value = getattr(self, field.name)
114
+ if isinstance(value, str):
115
+ bad = [c for c in _FORBIDDEN_CHARS if c in value]
116
+ if bad:
117
+ raise ValueError(
118
+ f"AdminTheme.{field.name} contains forbidden "
119
+ f"character(s) {bad!r}; these would break the "
120
+ f"injected <style>/HTML. Got: {value!r}"
121
+ )
122
+
123
+ def css_variables(self) -> dict[str, str]:
124
+ """Return the ``:root`` custom properties this theme sets.
125
+
126
+ Returns:
127
+ dict[str, str]: Mapping of CSS variable name to value. Only
128
+ the always-present variables are included here; mode- and
129
+ font-dependent rules are emitted by :meth:`to_css`.
130
+ """
131
+ variables: dict[str, str] = {
132
+ "--tempest-accent": self.accent,
133
+ "--tempest-accent-hover": self.accent_hover,
134
+ "--tempest-danger": self.danger,
135
+ "--tempest-bg": self.header_bg,
136
+ "--tempest-bg-soft": self.sidebar_bg or self.header_bg,
137
+ "--tempest-radius": self.radius,
138
+ }
139
+ if self.font_family is not None:
140
+ variables["--tempest-font"] = self.font_family
141
+ if self.page_bg is not None:
142
+ variables["--tempest-page-bg"] = self.page_bg
143
+ return variables
144
+
145
+ def to_css(self) -> str:
146
+ """Render the full ``<style>`` body for this theme.
147
+
148
+ Combines the :meth:`css_variables` ``:root`` block with the
149
+ optional dark-mode surface overrides. The result is injected
150
+ verbatim inside a ``<style>`` element in the page ``<head>``.
151
+
152
+ Returns:
153
+ str: CSS text. Empty-safe — a default ``AdminTheme()`` still
154
+ returns a valid (no-op-equivalent) block.
155
+ """
156
+ root_lines = "\n".join(
157
+ f" {name}: {value};" for name, value in self.css_variables().items()
158
+ )
159
+ blocks: list[str] = [f":root {{\n{root_lines}\n}}"]
160
+
161
+ if self.dark_mode and self.page_bg is None:
162
+ # Dark surfaces for the content area. The header/sidebar are
163
+ # already dark via --tempest-bg. Skipped when the project
164
+ # pinned its own page_bg (explicit value wins).
165
+ blocks.append(
166
+ ":root {\n"
167
+ " --tempest-page-bg: #0b1120;\n"
168
+ " --tempest-bg-row: #1e293b;\n"
169
+ " --tempest-bg-row-alt: #172033;\n"
170
+ " --tempest-fg: #e2e8f0;\n"
171
+ " --tempest-fg-soft: #94a3b8;\n"
172
+ "}\n"
173
+ "body { color: var(--tempest-fg); }\n"
174
+ "input, select, textarea {\n"
175
+ " background: #1e293b;\n"
176
+ " color: var(--tempest-fg);\n"
177
+ " border-color: #334155;\n"
178
+ "}"
179
+ )
180
+
181
+ if self.font_family is not None:
182
+ blocks.append("body { font-family: var(--tempest-font); }")
183
+
184
+ return "\n".join(blocks)
@@ -45,6 +45,14 @@ RUN useradd --create-home --uid 1000 app
45
45
  WORKDIR /app
46
46
  COPY --from=builder --chown=app:app /app /app
47
47
 
48
+ # WORKDIR created /app as root before the COPY, and `COPY --chown` only sets
49
+ # ownership on the copied *contents* — not on the pre-existing /app directory
50
+ # node itself. So the app user cannot create new entries (the runtime logs/
51
+ # dir, the default SQLite app.db, etc.) inside /app and the app crashes at
52
+ # startup with `PermissionError: [Errno 13] ... 'logs'`. Chown the directory
53
+ # and pre-create logs/ so the SDK's file logging (LOG_DIR=logs) works.
54
+ RUN mkdir -p /app/logs && chown -R app:app /app
55
+
48
56
  # Put the venv on PATH so `python` resolves to the project interpreter.
49
57
  # SERVER_HOST=0.0.0.0 binds all interfaces inside the container so the app
50
58
  # is reachable from the host (overridable via .env / `environment:`).