feat: 添加审计日志auditlog表

This commit is contained in:
caoqianming 2023-12-07 09:38:24 +08:00
parent d9ddefe5db
commit ba34b26928
6 changed files with 138 additions and 8 deletions

View File

@ -0,0 +1,31 @@
# Generated by Django 3.2.12 on 2023-12-07 01:35
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('monitor', '0003_alter_drfrequestlog_view_method'),
]
operations = [
migrations.CreateModel(
name='AuditLog',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('action', models.CharField(max_length=20, verbose_name='动作')),
('model_name', models.CharField(max_length=20, verbose_name='模型名')),
('instance_id', models.CharField(editable=False, max_length=20, verbose_name='记录ID')),
('change_reason', models.CharField(default='', max_length=50, verbose_name='变更原因')),
('change_time', models.DateTimeField(verbose_name='变更时间')),
('val_new', models.JSONField(default=dict, verbose_name='变更后完整数据')),
('difference', models.JSONField(default=list, verbose_name='变更情况')),
('change_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='操作人')),
],
),
]

View File

@ -4,6 +4,19 @@ from django.db import models
from apps.utils.models import BaseModel from apps.utils.models import BaseModel
class AuditLog(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
action = models.CharField('动作', max_length=20)
model_name = models.CharField('模型名', max_length=20)
instance_id = models.CharField('记录ID', max_length=20, editable=False)
change_reason = models.CharField('变更原因', default='', max_length=50)
change_user = models.ForeignKey(
'system.user', on_delete=models.SET_NULL, verbose_name='操作人', null=True, blank=True)
change_time = models.DateTimeField('变更时间')
val_new = models.JSONField('变更后完整数据', default=dict)
difference = models.JSONField('变更情况', default=list)
class DrfRequestLog(BaseModel): class DrfRequestLog(BaseModel):
"""Logs Django rest framework API requests""" """Logs Django rest framework API requests"""
@ -42,7 +55,8 @@ class DrfRequestLog(BaseModel):
response = models.TextField(null=True, blank=True) response = models.TextField(null=True, blank=True)
errors = models.TextField(null=True, blank=True) errors = models.TextField(null=True, blank=True)
agent = models.TextField(null=True, blank=True) agent = models.TextField(null=True, blank=True)
status_code = models.PositiveIntegerField(null=True, blank=True, db_index=True) status_code = models.PositiveIntegerField(
null=True, blank=True, db_index=True)
class Meta: class Meta:
verbose_name = "DRF请求日志" verbose_name = "DRF请求日志"

View File

@ -1,4 +1,14 @@
from rest_framework import serializers from rest_framework import serializers
from apps.utils.serializers import CustomModelSerializer
from apps.monitor.models import AuditLog
class DbbackupDeleteSerializer(serializers.Serializer): class DbbackupDeleteSerializer(serializers.Serializer):
filepaths = serializers.ListField(child=serializers.CharField(), label="文件地址列表") filepaths = serializers.ListField(
child=serializers.CharField(), label="文件地址列表")
class AuditLogSerializer(CustomModelSerializer):
class Meta:
model = AuditLog
fields = '__all__'

View File

@ -1,4 +1,63 @@
import psutil import psutil
from apps.monitor.models import AuditLog
from apps.system.models import User
from datetime import datetime
from apps.utils.tools import compare_values
from apps.utils.models import get_model_info
def delete_auditlog(model, instance_id):
"""
删除其对应的审计记录
"""
model_name = get_model_info(model)
AuditLog.objects.filter(model_name=model_name,
instance_id=instance_id).delete()
def create_auditlog(action: str, instance, val_new: dict, val_old: dict = None, change_reason: str = '', delete_time: datetime = None, delete_user: User = None):
"""
生成审计日志
action: create/update/delete/其他action
"""
app_label_model_name = get_model_info(instance)
if val_old is None:
val_old = {}
difference = []
has_changed = False
if action == 'create':
has_changed = True
change_user = instance.create_by
change_time = instance.create_time
elif action == 'delete':
has_changed = True
change_user = delete_user if delete_user else instance.update_by
change_time = delete_time if delete_time else instance.update_time
else:
change_user = instance.update_by
change_time = instance.update_time
for k, v in val_new.items():
if k not in ['create_by', 'update_by', 'create_time', 'update_time', 'id']:
if k not in val_old:
difference.append(
{'field': k, 'action': 'create', 'val_old': None, 'val_new': v})
elif not compare_values(val_new.get(k), val_old.get(k), ignore_order=True):
difference.append(
{'field': k, 'action': 'update', 'val_old': val_old[k], 'val_new': v})
if difference:
has_changed = True
if has_changed:
AuditLog.objects.create(
action=action,
model_name=app_label_model_name,
instance_id=instance.id,
val_new=val_new,
difference=difference,
change_reason=change_reason,
change_user=change_user,
change_time=change_time
)
class ServerService: class ServerService:
@classmethod @classmethod
@ -17,7 +76,7 @@ class ServerService:
ret['count'] = psutil.cpu_count(logical=False) ret['count'] = psutil.cpu_count(logical=False)
ret['percent'] = psutil.cpu_percent(interval=1) ret['percent'] = psutil.cpu_percent(interval=1)
return ret return ret
@classmethod @classmethod
def get_disk_dict(cls): def get_disk_dict(cls):
ret = {} ret = {}
@ -29,4 +88,4 @@ class ServerService:
@classmethod @classmethod
def get_full(cls): def get_full(cls):
return {'cpu': cls.get_cpu_dict(), 'memory': cls.get_memory_dict(), 'disk': cls.get_disk_dict()} return {'cpu': cls.get_cpu_dict(), 'memory': cls.get_memory_dict(), 'disk': cls.get_disk_dict()}

View File

@ -1,5 +1,5 @@
from django.urls import path from django.urls import path
from .views import DrfRequestLogViewSet, ServerInfoView, LogView, LogDetailView, index, room, video, DbBackupView from .views import DrfRequestLogViewSet, ServerInfoView, LogView, LogDetailView, index, room, video, DbBackupView, AuditlogViewSet
API_BASE_URL = 'api/monitor/' API_BASE_URL = 'api/monitor/'
HTML_BASE_URL = 'monitor/' HTML_BASE_URL = 'monitor/'
@ -13,5 +13,8 @@ urlpatterns = [
path(API_BASE_URL + 'log/<str:name>/', LogDetailView.as_view()), path(API_BASE_URL + 'log/<str:name>/', LogDetailView.as_view()),
path(API_BASE_URL + 'dbbackup/', DbBackupView.as_view()), path(API_BASE_URL + 'dbbackup/', DbBackupView.as_view()),
path(API_BASE_URL + 'server/', ServerInfoView.as_view()), path(API_BASE_URL + 'server/', ServerInfoView.as_view()),
path(API_BASE_URL + 'request_log/', DrfRequestLogViewSet.as_view({'get': 'list'}), name='requestlog_view') path(API_BASE_URL + 'request_log/',
DrfRequestLogViewSet.as_view({'get': 'list'}), name='requestlog_view'),
path(API_BASE_URL + 'auditlog/',
AuditlogViewSet.as_view({'get': 'list'}), name='auditlog_view')
] ]

View File

@ -7,13 +7,13 @@ from rest_framework.permissions import IsAuthenticated
from django.conf import settings from django.conf import settings
import os import os
from rest_framework import serializers from rest_framework import serializers
from apps.monitor.serializers import DbbackupDeleteSerializer from apps.monitor.serializers import DbbackupDeleteSerializer, AuditLogSerializer
from drf_yasg import openapi from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema from drf_yasg.utils import swagger_auto_schema
from rest_framework.exceptions import NotFound from rest_framework.exceptions import NotFound
from rest_framework.mixins import ListModelMixin from rest_framework.mixins import ListModelMixin
from apps.monitor.filters import DrfLogFilterSet from apps.monitor.filters import DrfLogFilterSet
from apps.monitor.models import DrfRequestLog from apps.monitor.models import DrfRequestLog, AuditLog
from apps.monitor.errors import LOG_NOT_FONED from apps.monitor.errors import LOG_NOT_FONED
from apps.monitor.services import ServerService from apps.monitor.services import ServerService
@ -188,3 +188,16 @@ class DrfRequestLogViewSet(ListModelMixin, CustomGenericViewSet):
ordering = ['-requested_at'] ordering = ['-requested_at']
filterset_class = DrfLogFilterSet filterset_class = DrfLogFilterSet
search_fields = ['path', 'view'] search_fields = ['path', 'view']
class AuditlogViewSet(ListModelMixin, CustomGenericViewSet):
"""审计日志
审计日志
"""
perms_map = {'get': '*'}
queryset = AuditLog.objects.all()
list_serializer_class = AuditLogSerializer
ordering = ['-change_time']
filterset_fields = ['change_user']
search_fields = ['model_name', 'action']