feat: audit ai config changes

This commit is contained in:
QiuSW
2026-07-02 11:42:39 +08:00
parent 18ac054dc5
commit c0020e8d29
16 changed files with 520 additions and 23 deletions
+136 -1
View File
@@ -1,7 +1,8 @@
from django import forms
from django.contrib import admin
from .models import AiModel, ModelAlias
from .audit import create_config_audit_log, snapshot_config
from .models import AiConfigAuditLog, AiModel, ModelAlias
from .security import AiKeyEncryptionError, encrypt_api_key
@@ -98,6 +99,53 @@ class AiModelAdmin(admin.ModelAdmin):
def api_key_status(self, obj):
return obj.api_key_masked or "not set"
def save_model(self, request, obj, form, change):
before = _snapshot_existing(obj) if change else None
super().save_model(request, obj, form, change)
create_config_audit_log(
actor=request.user,
action=(
AiConfigAuditLog.Action.UPDATE
if change
else AiConfigAuditLog.Action.CREATE
),
instance=obj,
before=before,
after=snapshot_config(obj),
)
def delete_model(self, request, obj):
before = snapshot_config(obj)
target_id = obj.pk
target_repr = str(obj)
super().delete_model(request, obj)
create_config_audit_log(
actor=request.user,
action=AiConfigAuditLog.Action.DELETE,
instance=obj,
before=before,
after=None,
target_id=target_id,
target_repr=target_repr,
)
def delete_queryset(self, request, queryset):
snapshots = [
(obj, obj.pk, str(obj), snapshot_config(obj))
for obj in queryset
]
super().delete_queryset(request, queryset)
for obj, target_id, target_repr, before in snapshots:
create_config_audit_log(
actor=request.user,
action=AiConfigAuditLog.Action.DELETE,
instance=obj,
before=before,
after=None,
target_id=target_id,
target_repr=target_repr,
)
@admin.register(ModelAlias)
class ModelAliasAdmin(admin.ModelAdmin):
@@ -113,3 +161,90 @@ class ModelAliasAdmin(admin.ModelAdmin):
search_fields = ("alias", "ai_model__name", "ai_model__model")
autocomplete_fields = ("ai_model",)
readonly_fields = ("created_at", "updated_at")
def save_model(self, request, obj, form, change):
before = _snapshot_existing(obj) if change else None
super().save_model(request, obj, form, change)
create_config_audit_log(
actor=request.user,
action=(
AiConfigAuditLog.Action.UPDATE
if change
else AiConfigAuditLog.Action.CREATE
),
instance=obj,
before=before,
after=snapshot_config(obj),
)
def delete_model(self, request, obj):
before = snapshot_config(obj)
target_id = obj.pk
target_repr = str(obj)
super().delete_model(request, obj)
create_config_audit_log(
actor=request.user,
action=AiConfigAuditLog.Action.DELETE,
instance=obj,
before=before,
after=None,
target_id=target_id,
target_repr=target_repr,
)
def delete_queryset(self, request, queryset):
snapshots = [
(obj, obj.pk, str(obj), snapshot_config(obj))
for obj in queryset
]
super().delete_queryset(request, queryset)
for obj, target_id, target_repr, before in snapshots:
create_config_audit_log(
actor=request.user,
action=AiConfigAuditLog.Action.DELETE,
instance=obj,
before=before,
after=None,
target_id=target_id,
target_repr=target_repr,
)
@admin.register(AiConfigAuditLog)
class AiConfigAuditLogAdmin(admin.ModelAdmin):
list_display = (
"created_at",
"actor",
"action",
"target_type",
"target_id",
"target_repr",
"changed_fields_display",
)
list_filter = ("action", "target_type", "created_at")
search_fields = (
"target_repr",
"actor__username",
"actor__email",
)
ordering = ("-created_at", "-id")
def get_readonly_fields(self, request, obj=None):
return tuple(field.name for field in self.model._meta.fields)
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
@admin.display(description="changed fields")
def changed_fields_display(self, obj):
return ", ".join(obj.changed_fields)
def _snapshot_existing(obj):
return snapshot_config(obj.__class__.objects.get(pk=obj.pk))