perf: 优化操作日志 (#12249)

* perf: 优化操作日志

* perf: 修改migrations中关于Nodes的verbose_name

* perf: 优化代码逻辑

* perf: 优化日志详情展示逻辑

* perf: 代码优雅一下

---------

Co-authored-by: jiangweidong <weidong.jiang@fit2cloud.com>
pull/12280/head
fit2bot 2023-12-05 17:26:47 +08:00 committed by GitHub
parent e193d7a942
commit 8f82ca9856
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 58 additions and 40 deletions

View File

@ -123,7 +123,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='asset',
name='nodes',
field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'),
field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Node'),
),
migrations.AddField(
model_name='systemuser',

View File

@ -50,7 +50,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='asset',
name='nodes',
field=models.ManyToManyField(default=assets.models.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'),
field=models.ManyToManyField(default=assets.models.default_node, related_name='assets', to='assets.Node', verbose_name='Node'),
),
migrations.AddField(
model_name='systemuser',

View File

@ -31,7 +31,7 @@ class Migration(migrations.Migration):
('type', models.CharField(max_length=16, verbose_name='Type')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('assets', models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets')),
('nodes', models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Nodes')),
('nodes', models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Node')),
],
options={
'verbose_name': 'Automation task',

View File

@ -162,7 +162,7 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets',
verbose_name=_("Domain"), on_delete=models.SET_NULL)
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets',
verbose_name=_("Nodes"))
verbose_name=_("Node"))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
gathered_info = models.JSONField(verbose_name=_('Gathered info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息
custom_info = models.JSONField(verbose_name=_('Custom info'), default=dict)

View File

@ -15,7 +15,7 @@ from orgs.mixins.models import OrgModelMixin, JMSOrgBaseModel
class BaseAutomation(PeriodTaskModelMixin, JMSOrgBaseModel):
accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Node"))
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
type = models.CharField(max_length=16, verbose_name=_('Type'))
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))

View File

@ -29,7 +29,7 @@ class CommandFilter(OrgModelMixin):
)
nodes = models.ManyToManyField(
'assets.Node', related_name='cmd_filters', blank=True,
verbose_name=_("Nodes")
verbose_name=_("Node")
)
assets = models.ManyToManyField(
'assets.Asset', related_name='cmd_filters', blank=True,

View File

@ -2,6 +2,7 @@
from django.utils.translation import gettext_lazy as _
from audits.models import OperateLog
from perms.const import ActionChoices
class OperateLogStore(object):
@ -45,20 +46,29 @@ class OperateLogStore(object):
before[k], after[k] = before_value, after_value
return before, after
@staticmethod
def _get_special_handler(resource_type):
# 根据资源类型,处理特殊字段
resource_map = {
'Asset permission': lambda k, v: ActionChoices.display(int(v)) if k == 'Actions' else v
}
return resource_map.get(resource_type, lambda k, v: v)
@classmethod
def convert_diff_friendly(cls, raw_diff):
def convert_diff_friendly(cls, op_log):
diff_list = list()
for k, v in raw_diff.items():
handler = cls._get_special_handler(op_log.resource_type)
for k, v in op_log.diff.items():
before, after = v.split(cls.SEP, 1)
diff_list.append({
'field': _(k),
'before': before if before else _('empty'),
'after': after if after else _('empty'),
'before': handler(k, before) if before else _('empty'),
'after': handler(k, after) if after else _('empty'),
})
return diff_list
def save(self, **kwargs):
log_id = kwargs.pop('id', None)
log_id = kwargs.get('id', None)
before = kwargs.pop('before') or {}
after = kwargs.pop('after') or {}

View File

@ -77,10 +77,7 @@ class OperateLogActionDetailSerializer(serializers.ModelSerializer):
fields = ('diff',)
def to_representation(self, instance):
data = super().to_representation(instance)
diff = OperateLogStore.convert_diff_friendly(data['diff'])
data['diff'] = diff
return data
return {'diff': OperateLogStore.convert_diff_friendly(instance)}
class OperateLogSerializer(BulkOrgResourceModelSerializer):

View File

@ -33,7 +33,9 @@ def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs):
with translation.override('en'):
resource_type = instance._meta.verbose_name
current_instance = model_to_dict(instance, include_model_fields=False)
current_instance = model_to_dict(
instance, include_model_fields=False, include_related_fields=[model]
)
instance_id = current_instance.get('id')
log_id, before_instance = get_instance_dict_from_cache(instance_id)
@ -176,7 +178,7 @@ def on_django_start_set_operate_log_monitor_models(sender, **kwargs):
'PermedAsset', 'PermedAccount', 'MenuPermission',
'Permission', 'TicketSession', 'ApplyLoginTicket',
'ApplyCommandTicket', 'ApplyLoginAssetTicket',
'FavoriteAsset',
'FavoriteAsset', 'Asset'
}
for i, app in enumerate(apps.get_models(), 1):
app_name = app._meta.app_label

View File

@ -78,31 +78,37 @@ def _get_instance_field_value(
return data
def model_to_dict_for_operate_log(instance, include_model_fields=True, include_related_fields=False):
model_need_continue_fields = ['date_updated']
m2m_need_continue_fields = ['history_passwords']
def model_to_dict_for_operate_log(
instance, include_model_fields=True, include_related_fields=None
):
def get_related_values(f):
value = []
if instance.pk is not None:
related_name = getattr(f, 'attname', '') or getattr(f, 'related_name', '')
if not related_name or related_name in ['history_passwords']:
return
try:
value = [str(i) for i in getattr(instance, related_name).all()]
except:
pass
if not value:
return
try:
field_key = getattr(f, 'verbose_name', None) or f.related_model._meta.verbose_name
data.setdefault(str(field_key), value)
except:
pass
data = _get_instance_field_value(
instance, include_model_fields, model_need_continue_fields
instance, include_model_fields, ['date_updated']
)
if include_related_fields:
opts = instance._meta
for f in opts.many_to_many:
value = []
if instance.pk is not None:
related_name = getattr(f, 'attname', '') or getattr(f, 'related_name', '')
if not related_name or related_name in m2m_need_continue_fields:
continue
try:
value = [str(i) for i in getattr(instance, related_name).all()]
except:
pass
if not value:
for f in chain(opts.many_to_many, opts.related_objects):
related_model = getattr(f, 'related_model', None)
if related_model not in include_related_fields:
continue
try:
field_key = getattr(f, 'verbose_name', None) or f.related_model._meta.verbose_name
data.setdefault(str(field_key), value)
except:
pass
get_related_values(f)
return data

View File

@ -584,7 +584,7 @@ class Config(dict):
'APPLET_DOWNLOAD_HOST': '',
# FTP 文件上传下载备份阈值,单位(M)当值小于等于0时不备份
'FTP_FILE_MAX_STORE': 100,
'FTP_FILE_MAX_STORE': 0,
# API 分页
'MAX_LIMIT_PER_PAGE': 10000,

View File

@ -80,7 +80,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='assetpermission',
name='nodes',
field=models.ManyToManyField(blank=True, related_name='granted_by_permissions', to='assets.Node', verbose_name='Nodes'),
field=models.ManyToManyField(blank=True, related_name='granted_by_permissions', to='assets.Node', verbose_name='Node'),
),
# migrations.RunPython(
# code=migrate_node_permissions,

View File

@ -69,7 +69,7 @@ class AssetPermission(LabeledMixin, JMSOrgBaseModel):
'assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset")
)
nodes = models.ManyToManyField(
'assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes")
'assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Node")
)
# 特殊的账号: @ALL, @INPUT @USER 默认包含,将来在全局设置中进行控制.
accounts = models.JSONField(default=list, verbose_name=_("Account"))

View File

@ -20,3 +20,6 @@ class Task(JMSBaseModel):
class Meta:
db_table = "terminal_task"
verbose_name = _("Task")
def __str__(self):
return str(dict(SessionTaskChoices.choices).get(self.name))