fastapi-admin-kit 0.2.1__tar.gz → 0.3.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.
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/PKG-INFO +1 -1
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/__init__.py +8 -1
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_database.py +11 -7
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_template.py +51 -17
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/core.py +31 -9
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/state.py +4 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/diff.py +14 -24
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/listener.py +15 -17
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/__init__.py +16 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/backend.py +0 -20
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/dependencies.py +28 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/hasher.py +10 -9
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/mixins.py +21 -24
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/auth/password.py +80 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/permissions.py +13 -2
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/auth/protocol.py +72 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/auth/types.py +24 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/views.py +1 -1
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/backends/__init__.py +63 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/backends/protocols.py +196 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/backends/sqlalchemy.py +678 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/permissions.py +5 -4
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/db.py +51 -23
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/filters/__init__.py +12 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/filters/base.py +335 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/filters/registry.py +163 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/form/pipeline.py +440 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/form/types.py +129 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/inline.py +111 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/inspection/__init__.py +37 -58
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/inspection/registry.py +144 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/inspection/types.py +37 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/inspection.py +1 -1
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/modeladmin.py +32 -4
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/base.py +14 -1
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/cursor.py +25 -11
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/dynamic.py +15 -6
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/offset.py +15 -5
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/router.py +3 -3
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/schemas/__init__.py +37 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/schemas/builtin.py +164 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/schemas/schema.py +119 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/search_utils.py +108 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/admin.css +22 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/js/admin.js +113 -1
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/form.html +13 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/templates/partials/inline_stacked.html +179 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/templates/partials/inline_tabular.html +180 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/types.py +46 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/ui/__init__.py +1 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/ui/types.py +35 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/__init__.py +80 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/audit.py +3 -11
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/class_views.py +354 -82
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/context.py +7 -241
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/factory.py +14 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/file_handler.py +96 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/form.py +89 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/list_context.py +459 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/model_saver.py +395 -0
- fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/renderers.py +423 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/roles.py +9 -17
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/sidebar.py +5 -2
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/users.py +9 -17
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views.py +1 -1
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/resolver.py +1 -1
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/auth/password.py +0 -37
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/auth/protocol.py +0 -22
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/filters/base.py +0 -170
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/filters/registry.py +0 -62
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/form/pipeline.py +0 -118
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/inspection/registry.py +0 -249
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/search_utils.py +0 -71
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/types.py +0 -160
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/__init__.py +0 -64
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/factory.py +0 -630
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/form.py +0 -161
- fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/renderers.py +0 -607
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/.gitignore +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/CHANGELOG.md +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/LICENSE +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/README.md +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/actions/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/actions/base.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/actions/registry.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_config.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_router.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/builtin_models.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/decorators.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/auth.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/crud.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/deps.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/roles.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/schema_generator.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/schemas.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/search.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/context.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/event_bus.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/events.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/logger.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/middleware.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/models.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/sqlalchemy_logger.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/csrf.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/identity.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/models.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/ratelimit.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/router.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/session.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/totp.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/__main__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/helpers.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/migrate.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/scaffold.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/user.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/audit.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/auth.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/behavior.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/database.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/nav.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/storage.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/theme.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/ui.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/dashboard/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/exceptions.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/field_types.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/flash.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/form/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/models/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/models/base.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/nav.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/plugins/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/py.typed +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry/core.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry/validation.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/presets.css +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/tokens.css +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/variables.css +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/icons/heroicons.svg +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/js/htmx-config.js +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/storage/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/storage/base.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/storage/local.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/base.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/form_fields.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/icons.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/table.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/widgets.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/2fa/setup.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/2fa/verify.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/403.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/audit_detail.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/audit_log.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/dashboard.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/detail.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/list.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/login.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/profile/password.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/profile/profile.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/role_form.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/roles/form.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/roles/list.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/roles.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/search.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/settings/theme.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/users/form.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/users/list.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/_inline_edit_row.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/command_palette.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/field_wrapper.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/flash_messages.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/head.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/head_minimal.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/inline_edit_form.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/list_table.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/mobile_backdrop.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/pagination.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/perm_select_widget.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/permission_widget.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/scripts.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/sidebar.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/topbar.html +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/validation.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/bulk.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/dashboard.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/delete.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/extra.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/list.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/profile.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/protocols.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/search.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/settings.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/totp.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/__init__.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/base.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/inputs.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/registry.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/relation.py +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/mkdocs.yml +0 -0
- {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-admin-kit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Drop-in admin panel for FastAPI + SQLAlchemy + SQLModel apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/borhanst/fastapi-admin-kit
|
|
6
6
|
Project-URL: Documentation, https://borhanst.github.io/fastapi-admin-kit/
|
|
@@ -5,6 +5,7 @@ from fastapi_admin_kit.admin.decorators import column
|
|
|
5
5
|
from fastapi_admin_kit.auth.mixins import AuthModelMixin
|
|
6
6
|
from fastapi_admin_kit.config import DatabaseConfig, DatabaseType
|
|
7
7
|
from fastapi_admin_kit.exceptions import ConfigError
|
|
8
|
+
from fastapi_admin_kit.inline import InlineModelAdmin, StackedInline, TabularInline
|
|
8
9
|
from fastapi_admin_kit.nav import (
|
|
9
10
|
BuiltNavGroup,
|
|
10
11
|
BuiltNavItem,
|
|
@@ -22,6 +23,7 @@ from fastapi_admin_kit.types import (
|
|
|
22
23
|
FieldsetContext,
|
|
23
24
|
FieldsetSpec,
|
|
24
25
|
FormContext,
|
|
26
|
+
InlineFormsetData,
|
|
25
27
|
PermissionSet,
|
|
26
28
|
RelationMeta,
|
|
27
29
|
SeedRole,
|
|
@@ -64,6 +66,11 @@ __all__ = [
|
|
|
64
66
|
"FieldsetContext",
|
|
65
67
|
"FieldsetSpec",
|
|
66
68
|
"FormContext",
|
|
69
|
+
"InlineFormsetData",
|
|
70
|
+
# Inline admin
|
|
71
|
+
"InlineModelAdmin",
|
|
72
|
+
"StackedInline",
|
|
73
|
+
"TabularInline",
|
|
67
74
|
# View classes
|
|
68
75
|
"BaseView",
|
|
69
76
|
"ListView",
|
|
@@ -75,4 +82,4 @@ __all__ = [
|
|
|
75
82
|
# Per-model assets
|
|
76
83
|
"AdminExtra",
|
|
77
84
|
]
|
|
78
|
-
__version__ = "0.
|
|
85
|
+
__version__ = "0.3.0"
|
{fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_database.py
RENAMED
|
@@ -6,6 +6,7 @@ import logging
|
|
|
6
6
|
import re
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyDatabaseBackend
|
|
9
10
|
from fastapi_admin_kit.config.database import DatabaseConfig
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
@@ -21,7 +22,11 @@ def _validate_identifier(name: str, kind: str = "table") -> str:
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class AdminDatabase:
|
|
24
|
-
"""Handles database setup, table creation, and role seeding.
|
|
25
|
+
"""Handles database setup, table creation, and role seeding.
|
|
26
|
+
|
|
27
|
+
Delegates engine creation, table creation, and auto-migration to
|
|
28
|
+
:class:`SqlAlchemyDatabaseBackend`.
|
|
29
|
+
"""
|
|
25
30
|
|
|
26
31
|
def __init__(
|
|
27
32
|
self,
|
|
@@ -32,11 +37,12 @@ class AdminDatabase:
|
|
|
32
37
|
self.engine = engine
|
|
33
38
|
self.base = base
|
|
34
39
|
self.database_config = database_config
|
|
40
|
+
self._backend = SqlAlchemyDatabaseBackend(
|
|
41
|
+
admin_database=self, database_config=database_config
|
|
42
|
+
)
|
|
35
43
|
|
|
36
44
|
def _ensure_engine(self) -> Any:
|
|
37
|
-
"""
|
|
38
|
-
Create the async engine from ``database_config`` if no engine is set.
|
|
39
|
-
"""
|
|
45
|
+
"""Create the async engine from ``database_config`` if no engine is set."""
|
|
40
46
|
if self.engine is None and self.database_config is not None:
|
|
41
47
|
self.engine = self.database_config.create_engine()
|
|
42
48
|
return self.engine
|
|
@@ -108,9 +114,7 @@ class AdminDatabase:
|
|
|
108
114
|
conn.execute(sql)
|
|
109
115
|
|
|
110
116
|
def _auto_migrate(self, sync_conn: Any, metadata: Any) -> None:
|
|
111
|
-
"""
|
|
112
|
-
Add missing columns to existing tables (sync, called via run_sync).
|
|
113
|
-
"""
|
|
117
|
+
"""Add missing columns to existing tables (sync, called via run_sync)."""
|
|
114
118
|
from sqlalchemy import inspect as sa_inspect
|
|
115
119
|
from sqlalchemy import text
|
|
116
120
|
|
{fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_template.py
RENAMED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"""Admin template management and context building."""
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
from typing import TYPE_CHECKING, Any
|
|
4
5
|
|
|
5
6
|
if TYPE_CHECKING:
|
|
6
7
|
pass
|
|
7
8
|
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
class AdminTemplate:
|
|
10
13
|
"""Manages Jinja2 template environment and sidebar context."""
|
|
@@ -46,11 +49,11 @@ class AdminTemplate:
|
|
|
46
49
|
jinja_env.env.filters["slugify"] = slugify
|
|
47
50
|
app.state.admin_jinja_env = jinja_env
|
|
48
51
|
|
|
49
|
-
def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
|
|
52
|
+
async def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
|
|
50
53
|
"""Thin wrapper — returns sidebar kwargs for TemplateResponse contexts."""
|
|
51
|
-
return self.build_sidebar_context(request)
|
|
54
|
+
return await self.build_sidebar_context(request)
|
|
52
55
|
|
|
53
|
-
def build_sidebar_context(
|
|
56
|
+
async def build_sidebar_context(
|
|
54
57
|
self,
|
|
55
58
|
request: Any,
|
|
56
59
|
user: Any = None,
|
|
@@ -84,28 +87,34 @@ class AdminTemplate:
|
|
|
84
87
|
permissions_map = {}
|
|
85
88
|
|
|
86
89
|
if user and not is_superuser:
|
|
90
|
+
user_id = snapshot.get("id") if snapshot else getattr(user, "id", None)
|
|
87
91
|
role_ids = (
|
|
88
92
|
snapshot.get("role_ids", []) if snapshot else getattr(user, "role_ids", [])
|
|
89
93
|
)
|
|
90
|
-
if role_ids:
|
|
94
|
+
if role_ids or user_id is not None:
|
|
91
95
|
try:
|
|
92
96
|
from sqlalchemy import select
|
|
93
|
-
from sqlalchemy.orm import Session
|
|
94
97
|
|
|
95
|
-
from fastapi_admin_kit.auth.models import
|
|
98
|
+
from fastapi_admin_kit.auth.models import (
|
|
99
|
+
Permission,
|
|
100
|
+
UserPermission,
|
|
101
|
+
admin_role_permissions,
|
|
102
|
+
)
|
|
103
|
+
from fastapi_admin_kit.db import get_db_session
|
|
104
|
+
|
|
105
|
+
session = get_db_session(request)
|
|
96
106
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
result =
|
|
107
|
+
# Load permissions from all roles, merge with OR logic
|
|
108
|
+
if role_ids:
|
|
109
|
+
result = await session.execute(
|
|
100
110
|
select(Permission)
|
|
101
111
|
.join(
|
|
102
112
|
admin_role_permissions,
|
|
103
113
|
Permission.id == admin_role_permissions.c.permission_id,
|
|
104
114
|
)
|
|
105
|
-
.
|
|
115
|
+
.where(admin_role_permissions.c.role_id.in_(role_ids))
|
|
106
116
|
)
|
|
107
|
-
|
|
108
|
-
for perm in rows:
|
|
117
|
+
for perm in result.scalars():
|
|
109
118
|
if perm.table_name in permissions_map:
|
|
110
119
|
existing = permissions_map[perm.table_name]
|
|
111
120
|
permissions_map[perm.table_name] = PermissionSet(
|
|
@@ -121,10 +130,35 @@ class AdminTemplate:
|
|
|
121
130
|
can_edit=perm.can_edit,
|
|
122
131
|
can_delete=perm.can_delete,
|
|
123
132
|
)
|
|
124
|
-
except Exception as exc:
|
|
125
|
-
import logging
|
|
126
133
|
|
|
127
|
-
|
|
134
|
+
# Load direct user permission overrides, merge on top
|
|
135
|
+
if user_id is not None:
|
|
136
|
+
result = await session.execute(
|
|
137
|
+
select(UserPermission, Permission)
|
|
138
|
+
.join(Permission, UserPermission.permission_id == Permission.id)
|
|
139
|
+
.where(UserPermission.user_id == user_id)
|
|
140
|
+
)
|
|
141
|
+
for up, perm in result:
|
|
142
|
+
table = perm.table_name
|
|
143
|
+
if table in permissions_map:
|
|
144
|
+
existing = permissions_map[table]
|
|
145
|
+
permissions_map[table] = PermissionSet(
|
|
146
|
+
can_view=existing.can_view or perm.can_view,
|
|
147
|
+
can_create=existing.can_create or perm.can_create,
|
|
148
|
+
can_edit=existing.can_edit or perm.can_edit,
|
|
149
|
+
can_delete=existing.can_delete or perm.can_delete,
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
permissions_map[table] = PermissionSet(
|
|
153
|
+
can_view=perm.can_view,
|
|
154
|
+
can_create=perm.can_create,
|
|
155
|
+
can_edit=perm.can_edit,
|
|
156
|
+
can_delete=perm.can_delete,
|
|
157
|
+
)
|
|
158
|
+
except Exception as exc:
|
|
159
|
+
logger.warning(
|
|
160
|
+
"Permission query failed in sidebar fallback: %s", exc, exc_info=True
|
|
161
|
+
)
|
|
128
162
|
|
|
129
163
|
def _item_visible(item: Any) -> bool:
|
|
130
164
|
return (
|
|
@@ -189,7 +223,7 @@ class AdminTemplate:
|
|
|
189
223
|
"settings_visible": settings_visible,
|
|
190
224
|
}
|
|
191
225
|
|
|
192
|
-
def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
|
|
226
|
+
async def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
|
|
193
227
|
"""Inject nav_groups + permissions_map into a template context dict."""
|
|
194
|
-
context.update(self.build_sidebar_context(request, user=user))
|
|
228
|
+
context.update(await self.build_sidebar_context(request, user=user))
|
|
195
229
|
return context
|
|
@@ -35,6 +35,7 @@ if TYPE_CHECKING:
|
|
|
35
35
|
from sqlalchemy.engine import Engine
|
|
36
36
|
|
|
37
37
|
from fastapi_admin_kit.auth.backend import AuthBackend
|
|
38
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyBackend
|
|
38
39
|
from fastapi_admin_kit.nav import NavGroupConfig, SidebarBuilder
|
|
39
40
|
from fastapi_admin_kit.storage.base import StorageBackend
|
|
40
41
|
from fastapi_admin_kit.views import ModelAdmin
|
|
@@ -124,6 +125,7 @@ class Admin:
|
|
|
124
125
|
database: AdminDatabase | None = None,
|
|
125
126
|
router: AdminRouter | None = None,
|
|
126
127
|
template: AdminTemplate | None = None,
|
|
128
|
+
backend: SqlAlchemyBackend | None = None,
|
|
127
129
|
# Legacy kwargs for backward compatibility
|
|
128
130
|
base: type | None = None,
|
|
129
131
|
title: str = "FastAPI Admin Kit",
|
|
@@ -288,6 +290,16 @@ class Admin:
|
|
|
288
290
|
self.router = router
|
|
289
291
|
self.template = template
|
|
290
292
|
|
|
293
|
+
# Backend: defaults to composed SqlAlchemyBackend
|
|
294
|
+
if backend is None:
|
|
295
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyBackend
|
|
296
|
+
|
|
297
|
+
backend = SqlAlchemyBackend.from_admin_database(database)
|
|
298
|
+
self.backend = backend
|
|
299
|
+
|
|
300
|
+
# Inject backend's introspection adapter into the registry's ModelInspector
|
|
301
|
+
self.registry.inspector._adapter = self.backend.introspection
|
|
302
|
+
|
|
291
303
|
# RBAC
|
|
292
304
|
self.seed_roles = seed_roles if seed_roles is not None else DEFAULT_SEED_ROLES
|
|
293
305
|
self.seed_roles_overwrite = seed_roles_overwrite
|
|
@@ -535,8 +547,6 @@ class Admin:
|
|
|
535
547
|
registered.admin.skip_auto_routes = True
|
|
536
548
|
|
|
537
549
|
# 8.3 Attach audit event listeners (after registry is populated)
|
|
538
|
-
from fastapi_admin_kit.audit.listener import attach_audit_listener
|
|
539
|
-
|
|
540
550
|
engine = self.database.engine
|
|
541
551
|
if engine is not None:
|
|
542
552
|
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
@@ -545,7 +555,10 @@ class Admin:
|
|
|
545
555
|
from fastapi_admin_kit.db import create_session_factory
|
|
546
556
|
|
|
547
557
|
session_factory = create_session_factory(engine)
|
|
548
|
-
|
|
558
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
|
|
559
|
+
|
|
560
|
+
audit_backend = SqlAlchemyAuditBackend()
|
|
561
|
+
audit_backend.attach_listeners(session_factory, self.registry)
|
|
549
562
|
|
|
550
563
|
# 9. Validate require_tags
|
|
551
564
|
if self.config.nav.require_tags:
|
|
@@ -703,6 +716,7 @@ class Admin:
|
|
|
703
716
|
admin_instance=self,
|
|
704
717
|
secret_key=self.router.secret_key,
|
|
705
718
|
session_samesite=self.config.auth.session_samesite,
|
|
719
|
+
backend=self.backend,
|
|
706
720
|
)
|
|
707
721
|
|
|
708
722
|
# Store typed state as single attribute
|
|
@@ -721,6 +735,14 @@ class Admin:
|
|
|
721
735
|
app.state.admin_jinja_env = state.jinja_env
|
|
722
736
|
# Unified signing-key source for sessions, CSRF, and JWT (see AdminState).
|
|
723
737
|
app.state.admin_secret_key = state.secret_key
|
|
738
|
+
# Multi-ORM backend: store composed backend and derive individual adapters
|
|
739
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemySessionAdapter
|
|
740
|
+
|
|
741
|
+
app.state.admin_backend = self.backend
|
|
742
|
+
app.state.admin_session_backend_class = SqlAlchemySessionAdapter
|
|
743
|
+
app.state.admin_query_adapter = self.backend.query
|
|
744
|
+
app.state.admin_introspection_adapter = self.backend.introspection
|
|
745
|
+
app.state.admin_audit_backend = self.backend.audit
|
|
724
746
|
|
|
725
747
|
# Wire the password hasher to the User model
|
|
726
748
|
from fastapi_admin_kit.auth.models import User
|
|
@@ -1018,21 +1040,21 @@ class Admin:
|
|
|
1018
1040
|
admin_path=self.router.admin_path,
|
|
1019
1041
|
)
|
|
1020
1042
|
|
|
1021
|
-
def build_sidebar_context(
|
|
1043
|
+
async def build_sidebar_context(
|
|
1022
1044
|
self,
|
|
1023
1045
|
request: Any,
|
|
1024
1046
|
user: Any = None,
|
|
1025
1047
|
permissions_map: dict | None = None,
|
|
1026
1048
|
) -> dict:
|
|
1027
1049
|
"""Build per-request sidebar context (RBAC filter + permissions map)."""
|
|
1028
|
-
return self.template.build_sidebar_context(
|
|
1050
|
+
return await self.template.build_sidebar_context(
|
|
1029
1051
|
request, user=user, permissions_map=permissions_map
|
|
1030
1052
|
)
|
|
1031
1053
|
|
|
1032
|
-
def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
|
|
1054
|
+
async def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
|
|
1033
1055
|
"""Thin wrapper — returns sidebar kwargs for TemplateResponse contexts."""
|
|
1034
|
-
return self.template.sidebar_template_kwargs(request)
|
|
1056
|
+
return await self.template.sidebar_template_kwargs(request)
|
|
1035
1057
|
|
|
1036
|
-
def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
|
|
1058
|
+
async def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
|
|
1037
1059
|
"""Inject nav_groups + permissions_map into a template context dict."""
|
|
1038
|
-
return self.template.apply_sidebar_context(request, user, context)
|
|
1060
|
+
return await self.template.apply_sidebar_context(request, user, context)
|
|
@@ -12,6 +12,7 @@ if TYPE_CHECKING:
|
|
|
12
12
|
from fastapi_admin_kit.admin.core import Admin
|
|
13
13
|
from fastapi_admin_kit.auth.backend import AuthBackend
|
|
14
14
|
from fastapi_admin_kit.auth.session import SignedCookieSessionBackend
|
|
15
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyBackend
|
|
15
16
|
from fastapi_admin_kit.registry import AdminRegistry
|
|
16
17
|
from fastapi_admin_kit.storage.base import StorageBackend
|
|
17
18
|
|
|
@@ -36,6 +37,8 @@ class AdminState:
|
|
|
36
37
|
# Unified signing-key source — used by signed-cookie sessions, CSRF, and JWT.
|
|
37
38
|
secret_key: str = ""
|
|
38
39
|
session_samesite: str = "strict"
|
|
40
|
+
# Multi-ORM backend — composes introspection, query, audit, database adapters.
|
|
41
|
+
backend: SqlAlchemyBackend | None = None
|
|
39
42
|
|
|
40
43
|
@classmethod
|
|
41
44
|
def from_request(cls, request: Any) -> AdminState:
|
|
@@ -73,4 +76,5 @@ class AdminState:
|
|
|
73
76
|
jinja_env=getattr(app_state, "admin_jinja_env", None),
|
|
74
77
|
admin_instance=getattr(app_state, "admin", None),
|
|
75
78
|
secret_key=getattr(app_state, "admin_secret_key", ""),
|
|
79
|
+
backend=getattr(app_state, "admin_backend", None),
|
|
76
80
|
)
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""Audit diff utilities — snapshot and diff computation for SQLAlchemy models.
|
|
1
|
+
"""Audit diff utilities — snapshot and diff computation for SQLAlchemy models.
|
|
2
|
+
|
|
3
|
+
``serialize_value()`` is ORM-agnostic and stays here.
|
|
4
|
+
``snapshot()`` and ``compute_diff()`` delegate to the active AuditBackend.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from __future__ import annotations
|
|
4
8
|
|
|
@@ -8,8 +12,6 @@ import enum
|
|
|
8
12
|
from typing import Any
|
|
9
13
|
from uuid import UUID
|
|
10
14
|
|
|
11
|
-
from sqlalchemy.inspection import inspect as sqlalchemy_inspect
|
|
12
|
-
|
|
13
15
|
|
|
14
16
|
def serialize_value(val: Any) -> Any:
|
|
15
17
|
"""Convert a value to a JSON-serializable form.
|
|
@@ -33,12 +35,9 @@ def serialize_value(val: Any) -> Any:
|
|
|
33
35
|
if isinstance(val, UUID):
|
|
34
36
|
return str(val)
|
|
35
37
|
if isinstance(val, bytes):
|
|
36
|
-
# For simplicity, we'll return the hex representation.
|
|
37
|
-
# Alternatively, we could use base64, but hex is simpler.
|
|
38
38
|
return val.hex()
|
|
39
39
|
if isinstance(val, enum.Enum):
|
|
40
40
|
return val.value
|
|
41
|
-
# For other types, we assume they are JSON serializable (or let JSON encoder handle it)
|
|
42
41
|
return val
|
|
43
42
|
|
|
44
43
|
|
|
@@ -46,18 +45,12 @@ def snapshot(obj: Any) -> dict[str, Any]:
|
|
|
46
45
|
"""Snapshot all mapped columns of a SQLAlchemy model instance.
|
|
47
46
|
|
|
48
47
|
Returns a dict mapping column name to serialized value.
|
|
48
|
+
Delegates to the active AuditBackend.
|
|
49
49
|
"""
|
|
50
|
-
|
|
51
|
-
raise ValueError("Object is not a SQLAlchemy model instance")
|
|
50
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
for column in mapper.columns:
|
|
56
|
-
# Skip foreign key columns that are represented by relationships?
|
|
57
|
-
# We'll include all columns for simplicity.
|
|
58
|
-
value = getattr(obj, column.key)
|
|
59
|
-
data[column.key] = serialize_value(value)
|
|
60
|
-
return data
|
|
52
|
+
backend = SqlAlchemyAuditBackend()
|
|
53
|
+
return backend.snapshot(obj)
|
|
61
54
|
|
|
62
55
|
|
|
63
56
|
def compute_diff(before: dict[str, Any], after: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -66,12 +59,9 @@ def compute_diff(before: dict[str, Any], after: dict[str, Any]) -> dict[str, Any
|
|
|
66
59
|
Returns a dict of changed fields, each containing:
|
|
67
60
|
{"old": <value>, "new": <value>}
|
|
68
61
|
Only fields that have changed are included.
|
|
62
|
+
Delegates to the active AuditBackend.
|
|
69
63
|
"""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
new_val = after.get(key)
|
|
75
|
-
if old_val != new_val:
|
|
76
|
-
diff[key] = {"old": old_val, "new": new_val}
|
|
77
|
-
return diff
|
|
64
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
|
|
65
|
+
|
|
66
|
+
backend = SqlAlchemyAuditBackend()
|
|
67
|
+
return backend.compute_diff(before, after)
|
|
@@ -55,31 +55,29 @@ def _snapshot_from_committed(obj: Any) -> dict[str, Any]:
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def _snapshot_current(obj: Any) -> dict[str, Any]:
|
|
58
|
-
"""Snapshot all mapped columns of a SQLAlchemy model instance.
|
|
59
|
-
if not hasattr(obj, "__table__"):
|
|
60
|
-
return {}
|
|
61
|
-
from sqlalchemy.inspection import inspect as sa_inspect
|
|
58
|
+
"""Snapshot all mapped columns of a SQLAlchemy model instance.
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
Delegates to the SqlAlchemyAuditBackend. Returns {} for non-model objects.
|
|
61
|
+
"""
|
|
62
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
|
|
63
|
+
|
|
64
|
+
backend = SqlAlchemyAuditBackend()
|
|
65
|
+
try:
|
|
66
|
+
return backend.snapshot(obj)
|
|
67
|
+
except ValueError:
|
|
68
|
+
return {}
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
def _compute_diff(before: dict[str, Any], after: dict[str, Any]) -> dict[str, Any]:
|
|
71
72
|
"""Compute changed fields between two snapshots.
|
|
72
73
|
|
|
73
74
|
Returns ``{"field": {"old": ..., "new": ...}}`` for each difference.
|
|
75
|
+
Delegates to the SqlAlchemyAuditBackend.
|
|
74
76
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
new_val = after.get(key)
|
|
80
|
-
if old_val != new_val:
|
|
81
|
-
diff[key] = {"old": old_val, "new": new_val}
|
|
82
|
-
return diff
|
|
77
|
+
from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
|
|
78
|
+
|
|
79
|
+
backend = SqlAlchemyAuditBackend()
|
|
80
|
+
return backend.compute_diff(before, after)
|
|
83
81
|
|
|
84
82
|
|
|
85
83
|
def _build_audit_row(
|
|
@@ -17,14 +17,28 @@ from fastapi_admin_kit.auth.models import (
|
|
|
17
17
|
User,
|
|
18
18
|
UserPermission,
|
|
19
19
|
)
|
|
20
|
+
from fastapi_admin_kit.auth.password import (
|
|
21
|
+
PasswordManager,
|
|
22
|
+
password_manager,
|
|
23
|
+
validate_password_strength,
|
|
24
|
+
)
|
|
25
|
+
from fastapi_admin_kit.auth.protocol import (
|
|
26
|
+
AdminPermissionProtocol,
|
|
27
|
+
AdminRoleProtocol,
|
|
28
|
+
AdminUserProtocol,
|
|
29
|
+
)
|
|
20
30
|
from fastapi_admin_kit.auth.session import (
|
|
21
31
|
SessionBackend,
|
|
22
32
|
SignedCookieSessionBackend,
|
|
23
33
|
)
|
|
24
34
|
|
|
25
35
|
__all__ = [
|
|
36
|
+
"AdminPermissionProtocol",
|
|
37
|
+
"AdminRoleProtocol",
|
|
38
|
+
"AdminUserProtocol",
|
|
26
39
|
"AuthModelMixin",
|
|
27
40
|
"BcryptHasher",
|
|
41
|
+
"PasswordManager",
|
|
28
42
|
"Permission",
|
|
29
43
|
"PasswordHasher",
|
|
30
44
|
"Role",
|
|
@@ -36,6 +50,8 @@ __all__ = [
|
|
|
36
50
|
"SignedCookieSessionBackend",
|
|
37
51
|
"auth_redirect_handler",
|
|
38
52
|
"forbidden_handler",
|
|
53
|
+
"password_manager",
|
|
39
54
|
"require_csrf_token",
|
|
40
55
|
"set_csrf_cookie",
|
|
56
|
+
"validate_password_strength",
|
|
41
57
|
]
|
|
@@ -5,30 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from typing import TYPE_CHECKING, Any
|
|
7
7
|
|
|
8
|
-
import bcrypt
|
|
9
|
-
|
|
10
8
|
if TYPE_CHECKING:
|
|
11
9
|
from fastapi_admin_kit.auth.protocol import AdminUserProtocol
|
|
12
10
|
|
|
13
11
|
|
|
14
|
-
class _PasswordHasher:
|
|
15
|
-
"""Thin wrapper around bcrypt for hash/verify."""
|
|
16
|
-
|
|
17
|
-
@staticmethod
|
|
18
|
-
def hash(password: str) -> str:
|
|
19
|
-
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
|
|
20
|
-
|
|
21
|
-
@staticmethod
|
|
22
|
-
def verify(password: str, hashed: str) -> bool:
|
|
23
|
-
try:
|
|
24
|
-
return bcrypt.checkpw(password.encode(), hashed.encode())
|
|
25
|
-
except (ValueError, TypeError):
|
|
26
|
-
return False
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
pwd_context = _PasswordHasher()
|
|
30
|
-
|
|
31
|
-
|
|
32
12
|
class AuthBackend(ABC):
|
|
33
13
|
"""Abstract authentication backend — verify credentials & load users."""
|
|
34
14
|
|
|
@@ -144,3 +144,31 @@ async def require_superuser(
|
|
|
144
144
|
if not getattr(user, "is_superuser", False):
|
|
145
145
|
raise HTTPException(status_code=403, detail="Superuser access required.")
|
|
146
146
|
return user
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
# Permission checker resolver (single source — deduplicated from renderers/factory)
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def resolve_permission_checker(request: Request) -> Any:
|
|
155
|
+
"""Resolve a ``PermissionChecker`` for the current request.
|
|
156
|
+
|
|
157
|
+
Returns ``None`` if the user is not authenticated or the checker cannot be built.
|
|
158
|
+
This is the canonical implementation — import from here, not from renderers/factory.
|
|
159
|
+
"""
|
|
160
|
+
from fastapi_admin_kit.auth.identity import get_current_user_from_cookie
|
|
161
|
+
from fastapi_admin_kit.auth.permissions import PermissionChecker
|
|
162
|
+
|
|
163
|
+
user = await get_current_user_from_cookie(request)
|
|
164
|
+
if user is None:
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
from fastapi_admin_kit.db import get_db_session
|
|
168
|
+
|
|
169
|
+
async_session = get_db_session(request)
|
|
170
|
+
if async_session is None:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
snapshot = getattr(request.state, "admin_user_snapshot", None)
|
|
174
|
+
return PermissionChecker(session=async_session, user=user, user_snapshot=snapshot)
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""Password hasher — pluggable protocol + default bcrypt implementation.
|
|
1
|
+
"""Password hasher — pluggable protocol + default bcrypt implementation.
|
|
2
|
+
|
|
3
|
+
For new code, prefer ``fastapi_admin_kit.auth.password.password_manager``.
|
|
4
|
+
This module is kept for backward compatibility and custom hasher support.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from __future__ import annotations
|
|
4
8
|
|
|
@@ -25,19 +29,16 @@ class PasswordHasher(ABC):
|
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
class BcryptHasher(PasswordHasher):
|
|
28
|
-
"""Default hasher using bcrypt."""
|
|
32
|
+
"""Default hasher using bcrypt — delegates to PasswordManager."""
|
|
29
33
|
|
|
30
34
|
@staticmethod
|
|
31
35
|
def hash(password: str) -> str:
|
|
32
|
-
import
|
|
36
|
+
from fastapi_admin_kit.auth.password import password_manager
|
|
33
37
|
|
|
34
|
-
return
|
|
38
|
+
return password_manager.hash(password)
|
|
35
39
|
|
|
36
40
|
@staticmethod
|
|
37
41
|
def verify(password: str, hashed: str) -> bool:
|
|
38
|
-
import
|
|
42
|
+
from fastapi_admin_kit.auth.password import password_manager
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
return bcrypt.checkpw(password.encode(), hashed.encode())
|
|
42
|
-
except (ValueError, TypeError):
|
|
43
|
-
return False
|
|
44
|
+
return password_manager.verify(password, hashed)
|