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.
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/CHANGELOG.md +41 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/CLAUDE.md +14 -5
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/PKG-INFO +1 -1
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/admin.en.md +70 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/admin.md +70 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/logging.en.md +9 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/logging.md +9 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/pyproject.toml +1 -1
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/__init__.py +3 -1
- tempest_fastapi_sdk-0.72.0/tempest_fastapi_sdk/admin/__init__.py +45 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/site.py +9 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/static/admin.css +12 -2
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/base.html +16 -2
- tempest_fastapi_sdk-0.72.0/tempest_fastapi_sdk/admin/theme.py +184 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/Dockerfile.tmpl +8 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/logging.py +62 -18
- tempest_fastapi_sdk-0.72.0/tests/admin/test_theme.py +124 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/test_logging.py +43 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/uv.lock +1 -1
- tempest_fastapi_sdk-0.71.0/tempest_fastapi_sdk/admin/__init__.py +0 -43
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.github/workflows/ci.yml +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.github/workflows/docs.yml +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.github/workflows/release-pypi.yml +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.gitignore +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/.python-version +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/Makefile +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/README.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/architecture.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/architecture.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/changelog.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/changelog.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/contributing.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/contributing.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/index.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/index.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/installation.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/installation.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/index.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/index.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/api.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/api.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/business-rules.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/business-rules.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/domain.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/domain.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/flows.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/flows.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/index.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/learning/marketplace/index.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/migration.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/migration.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/audit-trail.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/audit-trail.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/auth-flow.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/auth-flow.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/br-helpers.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/br-helpers.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cache.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cache.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cli.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/cli.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/database.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/database.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/deploy-safety.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/deploy-safety.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/downloads.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/downloads.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/email.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/email.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/feature-flags.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/feature-flags.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http-client.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http-client.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/http.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/idempotency.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/idempotency.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/index.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/index.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/metrics.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/metrics.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/mfa.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/mfa.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/multi-tenant.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/multi-tenant.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/observability.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/observability.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/offline-sync.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/offline-sync.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/outbox.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/outbox.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/queue-tasks.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/queue-tasks.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/realtime.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/realtime.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/refresh-tokens.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/refresh-tokens.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/security.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/security.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/sessions.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/sessions.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/storage.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/storage.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/stored-files.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/stored-files.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/testing.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/testing.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/uploads.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/uploads.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/utilities.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/utilities.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/webpush.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/webpush.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/websocket.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/recipes/websocket.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/reference.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/reference.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/roadmap.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/roadmap.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/tutorial.en.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/docs/tutorial.md +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/mkdocs.yml +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/mkdocs_hooks/llmstxt.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/scripts/extract_recipe.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/auth.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/config.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/discovery.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/forms.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/router.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/session.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/dashboard.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/detail.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/form.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/list.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/login.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/logs.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/templates/mfa.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/cookies.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/dependencies/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/dependencies/auth.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/handlers.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/body_size.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/cors.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/csrf.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/graceful.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/idempotency.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/rate_limit.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/middlewares/request_id.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/oauth.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/health.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/logs.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/metrics.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/routers/tool_spec.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/server.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/static.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/tracing.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/api/webhooks.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/guards.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/locale.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/page_renderer.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/router.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/schemas.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/service.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/activation.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_error.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/activation_success.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/en-US/password_reset.html +0 -0
- {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
- {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
- {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
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_error.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/activation_success.html +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/auth/templates/pt-BR/password_reset.html +0 -0
- {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
- {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
- {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
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/decorator.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/invalidation.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cache/redis_manager.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/README.md.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/dockerignore.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/env.example.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/gitignore.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/main.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/pyproject.toml.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/api/app.py.tmpl +0 -0
- {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
- {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
- {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
- {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
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/controllers/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/core/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/core/exceptions.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/core/settings.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/db/__init__.py.tmpl +0 -0
- {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
- {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
- {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
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/schemas/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/server.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/services/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/src/utils/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/tests/__init__.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/_templates/tests/test_smoke.py.tmpl +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/db.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/docker_compose.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/generate.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/lint.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/main.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/new.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/secrets.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/src_layers.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/cli/user.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/controllers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/controllers/base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/context.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/core/enums.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/_alembic_templates/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/_alembic_templates/env.py.template +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/alembic_hooks.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/audit.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/backup.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/connection.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/migrations.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/mixins.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/model.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/outbox.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/repository.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/slow_query.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/tenant.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_model.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_recovery_code_model.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_refresh_token_model.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/db/user_token_model.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/conflict.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/forbidden.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/i18n.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/jwt.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/not_found.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/too_many_requests.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/unauthorized.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/upload.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/exceptions/validation.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/backends.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/dependencies.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/flags/service.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/py.typed +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/queue/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/queue/manager.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/link_headers.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/logs.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/pagination.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/schemas/response.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/services/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/services/base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/services/file_mixin.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/dependencies.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/middleware.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/router.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/schemas.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/service.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sessions/store.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/settings/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/settings/base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/settings/mixins.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sse/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/sse/event_stream.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/storage/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/storage/minio_client.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/tasks/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/tasks/manager.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/tasks/scheduler.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/testing/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/testing/database.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/client_ip.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/data/br_locations.json +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/datetime.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/dict.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/download.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/email.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/http_client.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/jwt.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/locations.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/log.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/metrics.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/opaque_token.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/password.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/regex.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/storage_backends.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/throttle.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/totp.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/utils/upload.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/webpush/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/webpush/dispatcher.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/webpush/schemas.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/hub.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/router.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/websockets/schemas.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_auth.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_discovery.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_forms.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_logs_nav.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_mfa.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_router.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_site.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/admin/test_user_model.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_body_size.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_cookies.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_cors.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_csrf.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_dependencies_auth.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_graceful.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_handlers.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_health_router.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_idempotency.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_jwt_dependency.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_logs_router.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_oauth.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_prometheus.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_rate_limit.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_rate_limit_extras.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_request_id_middleware.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_role_dependency.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_server.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_static.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_tool_spec.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_tracing.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_webhooks.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/api/test_webhooks_rsa.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_guards.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_locale.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_mfa.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_refresh.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_refresh_db.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/auth/test_service.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/test_decorator.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/test_invalidation.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cache/test_redis_manager.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_db.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_db_seed.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_docker_compose.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_generate.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_main.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_secrets.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/cli/test_user.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/conftest.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/controllers/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/controllers/test_base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/test_context.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/core/test_enums.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_alembic_hooks.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_audit.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_backup.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_bulk_ops.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_connection.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_migrations.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_mixins.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_model.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_outbox.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_repository.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_safe_upgrade.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_slow_query.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/db/test_tenant.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/exceptions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/exceptions/test_exceptions.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/exceptions/test_i18n.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/flags/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/flags/test_flags.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/queue/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/queue/test_manager.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_cursor_pagination.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_link_headers.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_pagination.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/schemas/test_response.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/services/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/services/test_base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/services/test_file_mixin.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sessions/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sessions/test_sessions.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/settings/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/settings/test_base.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/settings/test_mixins.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sse/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/sse/test_event_stream.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/storage/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/storage/test_minio_client.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/tasks/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/tasks/test_manager.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/tasks/test_scheduler.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/testing/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/testing/test_database.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_client_ip.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_datetime.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_dict.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_download.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_email.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_http_client.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_jwt.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_lazy_extras.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_locations.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_log.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_metrics.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_opaque_token.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_password.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_regex.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_storage_backends.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_throttle.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/utils/test_upload.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/webpush/__init__.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/webpush/test_dispatcher.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/webpush/test_schemas.py +0 -0
- {tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tests/websockets/__init__.py +0 -0
- {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`
|
|
166
|
-
user-defined row / bulk operations (Django `actions`, Nova
|
|
167
|
-
Today only 3 are hardcoded (activate / deactivate /
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
{tempest_fastapi_sdk-0.71.0 → tempest_fastapi_sdk-0.72.0}/tempest_fastapi_sdk/admin/static/admin.css
RENAMED
|
@@ -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') }}">
|
|
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>
|
|
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:`).
|