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.
Files changed (208) hide show
  1. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/PKG-INFO +1 -1
  2. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/__init__.py +8 -1
  3. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_database.py +11 -7
  4. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_template.py +51 -17
  5. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/core.py +31 -9
  6. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/state.py +4 -0
  7. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/diff.py +14 -24
  8. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/listener.py +15 -17
  9. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/__init__.py +16 -0
  10. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/backend.py +0 -20
  11. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/dependencies.py +28 -0
  12. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/hasher.py +10 -9
  13. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/mixins.py +21 -24
  14. fastapi_admin_kit-0.3.0/fastapi_admin_kit/auth/password.py +80 -0
  15. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/permissions.py +13 -2
  16. fastapi_admin_kit-0.3.0/fastapi_admin_kit/auth/protocol.py +72 -0
  17. fastapi_admin_kit-0.3.0/fastapi_admin_kit/auth/types.py +24 -0
  18. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/views.py +1 -1
  19. fastapi_admin_kit-0.3.0/fastapi_admin_kit/backends/__init__.py +63 -0
  20. fastapi_admin_kit-0.3.0/fastapi_admin_kit/backends/protocols.py +196 -0
  21. fastapi_admin_kit-0.3.0/fastapi_admin_kit/backends/sqlalchemy.py +678 -0
  22. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/permissions.py +5 -4
  23. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/db.py +51 -23
  24. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/filters/__init__.py +12 -0
  25. fastapi_admin_kit-0.3.0/fastapi_admin_kit/filters/base.py +335 -0
  26. fastapi_admin_kit-0.3.0/fastapi_admin_kit/filters/registry.py +163 -0
  27. fastapi_admin_kit-0.3.0/fastapi_admin_kit/form/pipeline.py +440 -0
  28. fastapi_admin_kit-0.3.0/fastapi_admin_kit/form/types.py +129 -0
  29. fastapi_admin_kit-0.3.0/fastapi_admin_kit/inline.py +111 -0
  30. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/inspection/__init__.py +37 -58
  31. fastapi_admin_kit-0.3.0/fastapi_admin_kit/inspection/registry.py +144 -0
  32. fastapi_admin_kit-0.3.0/fastapi_admin_kit/inspection/types.py +37 -0
  33. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/inspection.py +1 -1
  34. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/modeladmin.py +32 -4
  35. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/base.py +14 -1
  36. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/cursor.py +25 -11
  37. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/dynamic.py +15 -6
  38. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/offset.py +15 -5
  39. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/router.py +3 -3
  40. fastapi_admin_kit-0.3.0/fastapi_admin_kit/schemas/__init__.py +37 -0
  41. fastapi_admin_kit-0.3.0/fastapi_admin_kit/schemas/builtin.py +164 -0
  42. fastapi_admin_kit-0.3.0/fastapi_admin_kit/schemas/schema.py +119 -0
  43. fastapi_admin_kit-0.3.0/fastapi_admin_kit/search_utils.py +108 -0
  44. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/admin.css +22 -0
  45. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/js/admin.js +113 -1
  46. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/form.html +13 -0
  47. fastapi_admin_kit-0.3.0/fastapi_admin_kit/templates/partials/inline_stacked.html +179 -0
  48. fastapi_admin_kit-0.3.0/fastapi_admin_kit/templates/partials/inline_tabular.html +180 -0
  49. fastapi_admin_kit-0.3.0/fastapi_admin_kit/types.py +46 -0
  50. fastapi_admin_kit-0.3.0/fastapi_admin_kit/ui/__init__.py +1 -0
  51. fastapi_admin_kit-0.3.0/fastapi_admin_kit/ui/types.py +35 -0
  52. fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/__init__.py +80 -0
  53. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/audit.py +3 -11
  54. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/class_views.py +354 -82
  55. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/context.py +7 -241
  56. fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/factory.py +14 -0
  57. fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/file_handler.py +96 -0
  58. fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/form.py +89 -0
  59. fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/list_context.py +459 -0
  60. fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/model_saver.py +395 -0
  61. fastapi_admin_kit-0.3.0/fastapi_admin_kit/views/renderers.py +423 -0
  62. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/roles.py +9 -17
  63. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/sidebar.py +5 -2
  64. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/users.py +9 -17
  65. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views.py +1 -1
  66. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/resolver.py +1 -1
  67. fastapi_admin_kit-0.2.1/fastapi_admin_kit/auth/password.py +0 -37
  68. fastapi_admin_kit-0.2.1/fastapi_admin_kit/auth/protocol.py +0 -22
  69. fastapi_admin_kit-0.2.1/fastapi_admin_kit/filters/base.py +0 -170
  70. fastapi_admin_kit-0.2.1/fastapi_admin_kit/filters/registry.py +0 -62
  71. fastapi_admin_kit-0.2.1/fastapi_admin_kit/form/pipeline.py +0 -118
  72. fastapi_admin_kit-0.2.1/fastapi_admin_kit/inspection/registry.py +0 -249
  73. fastapi_admin_kit-0.2.1/fastapi_admin_kit/search_utils.py +0 -71
  74. fastapi_admin_kit-0.2.1/fastapi_admin_kit/types.py +0 -160
  75. fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/__init__.py +0 -64
  76. fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/factory.py +0 -630
  77. fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/form.py +0 -161
  78. fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/renderers.py +0 -607
  79. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/.gitignore +0 -0
  80. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/CHANGELOG.md +0 -0
  81. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/LICENSE +0 -0
  82. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/README.md +0 -0
  83. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/actions/__init__.py +0 -0
  84. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/actions/base.py +0 -0
  85. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/actions/registry.py +0 -0
  86. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/__init__.py +0 -0
  87. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_config.py +0 -0
  88. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/admin_router.py +0 -0
  89. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/builtin_models.py +0 -0
  90. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/admin/decorators.py +0 -0
  91. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/__init__.py +0 -0
  92. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/auth.py +0 -0
  93. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/crud.py +0 -0
  94. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/deps.py +0 -0
  95. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/roles.py +0 -0
  96. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/schema_generator.py +0 -0
  97. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/schemas.py +0 -0
  98. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/api/search.py +0 -0
  99. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/__init__.py +0 -0
  100. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/context.py +0 -0
  101. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/event_bus.py +0 -0
  102. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/events.py +0 -0
  103. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/logger.py +0 -0
  104. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/middleware.py +0 -0
  105. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/models.py +0 -0
  106. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/audit/sqlalchemy_logger.py +0 -0
  107. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/csrf.py +0 -0
  108. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/identity.py +0 -0
  109. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/models.py +0 -0
  110. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/ratelimit.py +0 -0
  111. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/router.py +0 -0
  112. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/session.py +0 -0
  113. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/auth/totp.py +0 -0
  114. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/__init__.py +0 -0
  115. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/__main__.py +0 -0
  116. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/helpers.py +0 -0
  117. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/migrate.py +0 -0
  118. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/scaffold.py +0 -0
  119. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/cli/user.py +0 -0
  120. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/__init__.py +0 -0
  121. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/audit.py +0 -0
  122. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/auth.py +0 -0
  123. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/behavior.py +0 -0
  124. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/database.py +0 -0
  125. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/nav.py +0 -0
  126. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/storage.py +0 -0
  127. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/theme.py +0 -0
  128. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/config/ui.py +0 -0
  129. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/dashboard/__init__.py +0 -0
  130. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/exceptions.py +0 -0
  131. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/field_types.py +0 -0
  132. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/flash.py +0 -0
  133. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/form/__init__.py +0 -0
  134. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/models/__init__.py +0 -0
  135. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/models/base.py +0 -0
  136. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/nav.py +0 -0
  137. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/pagination/__init__.py +0 -0
  138. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/plugins/__init__.py +0 -0
  139. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/py.typed +0 -0
  140. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry/__init__.py +0 -0
  141. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry/core.py +0 -0
  142. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry/validation.py +0 -0
  143. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/registry.py +0 -0
  144. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/presets.css +0 -0
  145. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/tokens.css +0 -0
  146. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/css/variables.css +0 -0
  147. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/icons/heroicons.svg +0 -0
  148. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/static/js/htmx-config.js +0 -0
  149. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/storage/__init__.py +0 -0
  150. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/storage/base.py +0 -0
  151. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/storage/local.py +0 -0
  152. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/base.html +0 -0
  153. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/form_fields.html +0 -0
  154. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/icons.html +0 -0
  155. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/table.html +0 -0
  156. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/macros/widgets.html +0 -0
  157. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/2fa/setup.html +0 -0
  158. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/2fa/verify.html +0 -0
  159. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/403.html +0 -0
  160. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/audit_detail.html +0 -0
  161. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/audit_log.html +0 -0
  162. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/dashboard.html +0 -0
  163. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/detail.html +0 -0
  164. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/list.html +0 -0
  165. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/login.html +0 -0
  166. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/profile/password.html +0 -0
  167. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/profile/profile.html +0 -0
  168. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/role_form.html +0 -0
  169. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/roles/form.html +0 -0
  170. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/roles/list.html +0 -0
  171. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/roles.html +0 -0
  172. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/search.html +0 -0
  173. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/settings/theme.html +0 -0
  174. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/users/form.html +0 -0
  175. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/pages/users/list.html +0 -0
  176. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/_inline_edit_row.html +0 -0
  177. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/command_palette.html +0 -0
  178. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/field_wrapper.html +0 -0
  179. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/flash_messages.html +0 -0
  180. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/head.html +0 -0
  181. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/head_minimal.html +0 -0
  182. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/inline_edit_form.html +0 -0
  183. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/list_table.html +0 -0
  184. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/mobile_backdrop.html +0 -0
  185. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/pagination.html +0 -0
  186. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/perm_select_widget.html +0 -0
  187. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/permission_widget.html +0 -0
  188. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/scripts.html +0 -0
  189. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/sidebar.html +0 -0
  190. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/templates/partials/topbar.html +0 -0
  191. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/validation.py +0 -0
  192. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/bulk.py +0 -0
  193. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/dashboard.py +0 -0
  194. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/delete.py +0 -0
  195. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/extra.py +0 -0
  196. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/list.py +0 -0
  197. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/profile.py +0 -0
  198. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/protocols.py +0 -0
  199. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/search.py +0 -0
  200. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/settings.py +0 -0
  201. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/views/totp.py +0 -0
  202. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/__init__.py +0 -0
  203. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/base.py +0 -0
  204. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/inputs.py +0 -0
  205. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/registry.py +0 -0
  206. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/fastapi_admin_kit/widgets/relation.py +0 -0
  207. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.0}/mkdocs.yml +0 -0
  208. {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.2.1
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.2.1"
85
+ __version__ = "0.3.0"
@@ -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
 
@@ -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 Permission, admin_role_permissions
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
- engine = request.app.state.admin_engine
98
- with Session(engine) as s:
99
- result = s.execute(
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
- .filter(admin_role_permissions.c.role_id.in_(role_ids))
115
+ .where(admin_role_permissions.c.role_id.in_(role_ids))
106
116
  )
107
- rows = result.scalars().all()
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
- logging.getLogger(__name__).debug("Permission query failed: %s", exc)
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
- attach_audit_listener(session_factory, self.registry)
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
- if not hasattr(obj, "__table__"):
51
- raise ValueError("Object is not a SQLAlchemy model instance")
50
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
52
51
 
53
- mapper = sqlalchemy_inspect(obj.__class__)
54
- data = {}
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
- diff = {}
71
- all_keys = set(before.keys()) | set(after.keys())
72
- for key in all_keys:
73
- old_val = before.get(key)
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
- mapper = sa_inspect(obj.__class__)
64
- data: dict[str, Any] = {}
65
- for column in mapper.columns:
66
- data[column.key] = serialize_value(getattr(obj, column.key))
67
- return data
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
- diff: dict[str, Any] = {}
76
- all_keys = set(before.keys()) | set(after.keys())
77
- for key in all_keys:
78
- old_val = before.get(key)
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 bcrypt
36
+ from fastapi_admin_kit.auth.password import password_manager
33
37
 
34
- return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
38
+ return password_manager.hash(password)
35
39
 
36
40
  @staticmethod
37
41
  def verify(password: str, hashed: str) -> bool:
38
- import bcrypt
42
+ from fastapi_admin_kit.auth.password import password_manager
39
43
 
40
- try:
41
- return bcrypt.checkpw(password.encode(), hashed.encode())
42
- except (ValueError, TypeError):
43
- return False
44
+ return password_manager.verify(password, hashed)