fastapi-admin-kit 0.1.0__py3-none-any.whl
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/__init__.py +73 -0
- fastapi_admin_kit/actions/__init__.py +63 -0
- fastapi_admin_kit/actions/base.py +68 -0
- fastapi_admin_kit/actions/registry.py +43 -0
- fastapi_admin_kit/admin/__init__.py +17 -0
- fastapi_admin_kit/admin/admin_config.py +95 -0
- fastapi_admin_kit/admin/admin_database.py +138 -0
- fastapi_admin_kit/admin/admin_router.py +74 -0
- fastapi_admin_kit/admin/admin_template.py +203 -0
- fastapi_admin_kit/admin/builtin_models.py +284 -0
- fastapi_admin_kit/admin/core.py +1036 -0
- fastapi_admin_kit/admin/decorators.py +70 -0
- fastapi_admin_kit/admin/state.py +76 -0
- fastapi_admin_kit/admin.py +728 -0
- fastapi_admin_kit/api/__init__.py +44 -0
- fastapi_admin_kit/api/auth.py +342 -0
- fastapi_admin_kit/api/crud.py +128 -0
- fastapi_admin_kit/api/deps.py +79 -0
- fastapi_admin_kit/api/roles.py +128 -0
- fastapi_admin_kit/api/schema_generator.py +171 -0
- fastapi_admin_kit/api/schemas.py +81 -0
- fastapi_admin_kit/api/search.py +132 -0
- fastapi_admin_kit/audit/__init__.py +36 -0
- fastapi_admin_kit/audit/context.py +62 -0
- fastapi_admin_kit/audit/diff.py +77 -0
- fastapi_admin_kit/audit/event_bus.py +96 -0
- fastapi_admin_kit/audit/events.py +48 -0
- fastapi_admin_kit/audit/listener.py +159 -0
- fastapi_admin_kit/audit/logger.py +28 -0
- fastapi_admin_kit/audit/middleware.py +39 -0
- fastapi_admin_kit/audit/models.py +53 -0
- fastapi_admin_kit/audit/sqlalchemy_logger.py +58 -0
- fastapi_admin_kit/auth/__init__.py +34 -0
- fastapi_admin_kit/auth/backend.py +95 -0
- fastapi_admin_kit/auth/csrf.py +240 -0
- fastapi_admin_kit/auth/dependencies.py +150 -0
- fastapi_admin_kit/auth/identity.py +181 -0
- fastapi_admin_kit/auth/models.py +246 -0
- fastapi_admin_kit/auth/password.py +35 -0
- fastapi_admin_kit/auth/permissions.py +205 -0
- fastapi_admin_kit/auth/protocol.py +22 -0
- fastapi_admin_kit/auth/ratelimit.py +88 -0
- fastapi_admin_kit/auth/router.py +10 -0
- fastapi_admin_kit/auth/session.py +79 -0
- fastapi_admin_kit/auth/totp.py +83 -0
- fastapi_admin_kit/auth/views.py +165 -0
- fastapi_admin_kit/cli.py +229 -0
- fastapi_admin_kit/config/__init__.py +19 -0
- fastapi_admin_kit/config/audit.py +18 -0
- fastapi_admin_kit/config/auth.py +54 -0
- fastapi_admin_kit/config/behavior.py +27 -0
- fastapi_admin_kit/config/nav.py +32 -0
- fastapi_admin_kit/config/storage.py +22 -0
- fastapi_admin_kit/config/theme.py +215 -0
- fastapi_admin_kit/config/ui.py +147 -0
- fastapi_admin_kit/dashboard/__init__.py +64 -0
- fastapi_admin_kit/db.py +133 -0
- fastapi_admin_kit/exceptions.py +5 -0
- fastapi_admin_kit/field_types.py +81 -0
- fastapi_admin_kit/filters/__init__.py +21 -0
- fastapi_admin_kit/filters/base.py +170 -0
- fastapi_admin_kit/filters/registry.py +68 -0
- fastapi_admin_kit/flash.py +45 -0
- fastapi_admin_kit/form/__init__.py +1 -0
- fastapi_admin_kit/form/pipeline.py +106 -0
- fastapi_admin_kit/inspection/__init__.py +117 -0
- fastapi_admin_kit/inspection/registry.py +253 -0
- fastapi_admin_kit/inspection.py +115 -0
- fastapi_admin_kit/modeladmin.py +375 -0
- fastapi_admin_kit/models/__init__.py +7 -0
- fastapi_admin_kit/models/base.py +7 -0
- fastapi_admin_kit/nav.py +208 -0
- fastapi_admin_kit/pagination/__init__.py +14 -0
- fastapi_admin_kit/pagination/base.py +40 -0
- fastapi_admin_kit/pagination/cursor.py +97 -0
- fastapi_admin_kit/pagination/dynamic.py +48 -0
- fastapi_admin_kit/pagination/offset.py +42 -0
- fastapi_admin_kit/plugins/__init__.py +1 -0
- fastapi_admin_kit/py.typed +0 -0
- fastapi_admin_kit/registry/__init__.py +5 -0
- fastapi_admin_kit/registry/core.py +287 -0
- fastapi_admin_kit/registry/validation.py +107 -0
- fastapi_admin_kit/registry.py +15 -0
- fastapi_admin_kit/router.py +335 -0
- fastapi_admin_kit/static/css/admin.css +4736 -0
- fastapi_admin_kit/static/css/presets.css +317 -0
- fastapi_admin_kit/static/css/tokens.css +217 -0
- fastapi_admin_kit/static/css/variables.css +74 -0
- fastapi_admin_kit/static/icons/heroicons.svg +160 -0
- fastapi_admin_kit/static/js/admin.js +692 -0
- fastapi_admin_kit/static/js/htmx-config.js +42 -0
- fastapi_admin_kit/storage/__init__.py +6 -0
- fastapi_admin_kit/storage/base.py +48 -0
- fastapi_admin_kit/storage/local.py +73 -0
- fastapi_admin_kit/templates/base.html +142 -0
- fastapi_admin_kit/templates/macros/form_fields.html +660 -0
- fastapi_admin_kit/templates/macros/icons.html +50 -0
- fastapi_admin_kit/templates/macros/table.html +108 -0
- fastapi_admin_kit/templates/macros/widgets.html +159 -0
- fastapi_admin_kit/templates/pages/2fa/setup.html +122 -0
- fastapi_admin_kit/templates/pages/2fa/verify.html +55 -0
- fastapi_admin_kit/templates/pages/audit_detail.html +122 -0
- fastapi_admin_kit/templates/pages/audit_log.html +102 -0
- fastapi_admin_kit/templates/pages/dashboard.html +295 -0
- fastapi_admin_kit/templates/pages/detail.html +183 -0
- fastapi_admin_kit/templates/pages/form.html +119 -0
- fastapi_admin_kit/templates/pages/list.html +277 -0
- fastapi_admin_kit/templates/pages/login.html +85 -0
- fastapi_admin_kit/templates/pages/profile/password.html +78 -0
- fastapi_admin_kit/templates/pages/profile/profile.html +73 -0
- fastapi_admin_kit/templates/pages/role_form.html +75 -0
- fastapi_admin_kit/templates/pages/roles/form.html +117 -0
- fastapi_admin_kit/templates/pages/roles/list.html +69 -0
- fastapi_admin_kit/templates/pages/roles.html +77 -0
- fastapi_admin_kit/templates/pages/settings/theme.html +255 -0
- fastapi_admin_kit/templates/pages/users/form.html +229 -0
- fastapi_admin_kit/templates/pages/users/list.html +83 -0
- fastapi_admin_kit/templates/partials/command_palette.html +52 -0
- fastapi_admin_kit/templates/partials/field_wrapper.html +2 -0
- fastapi_admin_kit/templates/partials/flash_messages.html +39 -0
- fastapi_admin_kit/templates/partials/head.html +21 -0
- fastapi_admin_kit/templates/partials/head_minimal.html +18 -0
- fastapi_admin_kit/templates/partials/list_table.html +178 -0
- fastapi_admin_kit/templates/partials/mobile_backdrop.html +2 -0
- fastapi_admin_kit/templates/partials/pagination.html +82 -0
- fastapi_admin_kit/templates/partials/permission_widget.html +86 -0
- fastapi_admin_kit/templates/partials/scripts.html +13 -0
- fastapi_admin_kit/templates/partials/sidebar.html +94 -0
- fastapi_admin_kit/templates/partials/topbar.html +95 -0
- fastapi_admin_kit/types.py +145 -0
- fastapi_admin_kit/validation.py +43 -0
- fastapi_admin_kit/views/__init__.py +78 -0
- fastapi_admin_kit/views/audit.py +134 -0
- fastapi_admin_kit/views/bulk.py +28 -0
- fastapi_admin_kit/views/class_views.py +1040 -0
- fastapi_admin_kit/views/context.py +588 -0
- fastapi_admin_kit/views/dashboard.py +162 -0
- fastapi_admin_kit/views/delete.py +31 -0
- fastapi_admin_kit/views/extra.py +65 -0
- fastapi_admin_kit/views/factory.py +667 -0
- fastapi_admin_kit/views/form.py +159 -0
- fastapi_admin_kit/views/list.py +28 -0
- fastapi_admin_kit/views/profile.py +219 -0
- fastapi_admin_kit/views/protocols.py +54 -0
- fastapi_admin_kit/views/renderers.py +634 -0
- fastapi_admin_kit/views/roles.py +230 -0
- fastapi_admin_kit/views/search.py +31 -0
- fastapi_admin_kit/views/settings.py +31 -0
- fastapi_admin_kit/views/sidebar.py +101 -0
- fastapi_admin_kit/views/totp.py +249 -0
- fastapi_admin_kit/views/users.py +347 -0
- fastapi_admin_kit/views.py +117 -0
- fastapi_admin_kit/widgets/__init__.py +44 -0
- fastapi_admin_kit/widgets/base.py +44 -0
- fastapi_admin_kit/widgets/inputs.py +363 -0
- fastapi_admin_kit/widgets/registry.py +110 -0
- fastapi_admin_kit/widgets/relation.py +70 -0
- fastapi_admin_kit/widgets/resolver.py +102 -0
- fastapi_admin_kit-0.1.0.dist-info/METADATA +210 -0
- fastapi_admin_kit-0.1.0.dist-info/RECORD +163 -0
- fastapi_admin_kit-0.1.0.dist-info/WHEEL +4 -0
- fastapi_admin_kit-0.1.0.dist-info/entry_points.txt +3 -0
- fastapi_admin_kit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Audit log views — list and detail."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import date, datetime
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
|
8
|
+
from sqlalchemy import desc, select
|
|
9
|
+
|
|
10
|
+
from fastapi_admin_kit.audit.models import AuditLog
|
|
11
|
+
from fastapi_admin_kit.auth.dependencies import get_current_admin_user
|
|
12
|
+
from fastapi_admin_kit.auth.protocol import AdminUserProtocol
|
|
13
|
+
from fastapi_admin_kit.db import get_db_session
|
|
14
|
+
from fastapi_admin_kit.views.sidebar import inject_sidebar_context
|
|
15
|
+
|
|
16
|
+
router = APIRouter()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def _require_superuser(
|
|
20
|
+
user: AdminUserProtocol = Depends(get_current_admin_user),
|
|
21
|
+
) -> AdminUserProtocol:
|
|
22
|
+
if not getattr(user, "is_superuser", False):
|
|
23
|
+
raise HTTPException(status_code=403, detail="Superuser access required.")
|
|
24
|
+
return user
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@router.get("/audit")
|
|
28
|
+
async def audit_list_view(
|
|
29
|
+
request: Request,
|
|
30
|
+
model: str | None = Query(None, description="Filter by model name"),
|
|
31
|
+
user_id: int | None = Query(None),
|
|
32
|
+
action: str | None = Query(None, regex="^(CREATE|UPDATE|DELETE)$"),
|
|
33
|
+
from_date: date | None = Query(None),
|
|
34
|
+
to_date: date | None = Query(None),
|
|
35
|
+
object_id: str | None = Query(None),
|
|
36
|
+
page: int = Query(1, ge=1),
|
|
37
|
+
_: AdminUserProtocol = Depends(_require_superuser),
|
|
38
|
+
):
|
|
39
|
+
"""List audit log entries with filters."""
|
|
40
|
+
templates = request.app.state.admin_jinja_env
|
|
41
|
+
session = get_db_session(request)
|
|
42
|
+
|
|
43
|
+
query = select(AuditLog)
|
|
44
|
+
|
|
45
|
+
if model:
|
|
46
|
+
query = query.where(AuditLog.table_name == model)
|
|
47
|
+
if user_id:
|
|
48
|
+
query = query.where(AuditLog.user_id == user_id)
|
|
49
|
+
if action:
|
|
50
|
+
query = query.where(AuditLog.action == action)
|
|
51
|
+
if from_date:
|
|
52
|
+
query = query.where(AuditLog.timestamp >= datetime.combine(from_date, datetime.min.time()))
|
|
53
|
+
if to_date:
|
|
54
|
+
query = query.where(AuditLog.timestamp <= datetime.combine(to_date, datetime.max.time()))
|
|
55
|
+
if object_id:
|
|
56
|
+
query = query.where(AuditLog.object_id == object_id)
|
|
57
|
+
|
|
58
|
+
query = query.order_by(desc(AuditLog.timestamp))
|
|
59
|
+
per_page = 25
|
|
60
|
+
offset = (page - 1) * per_page
|
|
61
|
+
total_query = select(AuditLog)
|
|
62
|
+
if model:
|
|
63
|
+
total_query = total_query.where(AuditLog.table_name == model)
|
|
64
|
+
if user_id:
|
|
65
|
+
total_query = total_query.where(AuditLog.user_id == user_id)
|
|
66
|
+
if action:
|
|
67
|
+
total_query = total_query.where(AuditLog.action == action)
|
|
68
|
+
if from_date:
|
|
69
|
+
total_query = total_query.where(
|
|
70
|
+
AuditLog.timestamp >= datetime.combine(from_date, datetime.min.time())
|
|
71
|
+
)
|
|
72
|
+
if to_date:
|
|
73
|
+
total_query = total_query.where(
|
|
74
|
+
AuditLog.timestamp <= datetime.combine(to_date, datetime.max.time())
|
|
75
|
+
)
|
|
76
|
+
if object_id:
|
|
77
|
+
total_query = total_query.where(AuditLog.object_id == object_id)
|
|
78
|
+
|
|
79
|
+
total_result = await session.execute(total_query)
|
|
80
|
+
total = len(total_result.scalars().all())
|
|
81
|
+
entries = (await session.execute(query.offset(offset).limit(per_page))).scalars().all()
|
|
82
|
+
|
|
83
|
+
admin_path = request.app.state.admin_config["admin_path"]
|
|
84
|
+
|
|
85
|
+
return templates.TemplateResponse(
|
|
86
|
+
request,
|
|
87
|
+
"pages/audit_log.html",
|
|
88
|
+
await inject_sidebar_context(request, {
|
|
89
|
+
"entries": entries,
|
|
90
|
+
"page": page,
|
|
91
|
+
"per_page": per_page,
|
|
92
|
+
"total_items": total,
|
|
93
|
+
"total_pages": max(1, (total + per_page - 1) // per_page),
|
|
94
|
+
"admin_path": admin_path,
|
|
95
|
+
"search": "",
|
|
96
|
+
"action_filter": action or "",
|
|
97
|
+
"model_filter": model or "",
|
|
98
|
+
"model_names": [m.table_name for m in request.app.state.admin_registry.all()],
|
|
99
|
+
"filters": {
|
|
100
|
+
"model": model or "",
|
|
101
|
+
"user_id": user_id or "",
|
|
102
|
+
"action": action or "",
|
|
103
|
+
"from_date": from_date or "",
|
|
104
|
+
"to_date": to_date or "",
|
|
105
|
+
"object_id": object_id or "",
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@router.get("/audit/{entry_id}")
|
|
112
|
+
async def audit_detail_view(
|
|
113
|
+
request: Request,
|
|
114
|
+
entry_id: int,
|
|
115
|
+
_: AdminUserProtocol = Depends(_require_superuser),
|
|
116
|
+
):
|
|
117
|
+
"""Show detailed audit entry with diff snapshot."""
|
|
118
|
+
templates = request.app.state.admin_jinja_env
|
|
119
|
+
session = get_db_session(request)
|
|
120
|
+
|
|
121
|
+
entry = await session.get(AuditLog, entry_id)
|
|
122
|
+
if entry is None:
|
|
123
|
+
raise HTTPException(status_code=404, detail="Audit entry not found")
|
|
124
|
+
|
|
125
|
+
admin_path = request.app.state.admin_config["admin_path"]
|
|
126
|
+
|
|
127
|
+
return templates.TemplateResponse(
|
|
128
|
+
request,
|
|
129
|
+
"pages/audit_detail.html",
|
|
130
|
+
await inject_sidebar_context(request, {
|
|
131
|
+
"entry": entry,
|
|
132
|
+
"admin_path": admin_path,
|
|
133
|
+
}),
|
|
134
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Bulk action handler factory for registered models.
|
|
2
|
+
|
|
3
|
+
Backward-compatible wrapper — delegates to BulkView class.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from fastapi import Request
|
|
11
|
+
|
|
12
|
+
from fastapi_admin_kit.registry import RegisteredModel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def bulk_factory(registered: RegisteredModel):
|
|
16
|
+
"""Create a bulk action handler — delegates to BulkView.html_response."""
|
|
17
|
+
from fastapi_admin_kit.views.class_views import BulkView, _resolve_view_class
|
|
18
|
+
|
|
19
|
+
view_class = _resolve_view_class(
|
|
20
|
+
registered.admin, "bulk_view_class", BulkView
|
|
21
|
+
)
|
|
22
|
+
view_instance = view_class(registered)
|
|
23
|
+
|
|
24
|
+
async def _handler(request: Request, **kwargs: Any):
|
|
25
|
+
return await view_instance.html_response(request, **kwargs)
|
|
26
|
+
|
|
27
|
+
_handler.__name__ = f"bulk_{registered.table_name}"
|
|
28
|
+
return _handler
|