mirror of https://github.com/jumpserver/jumpserver
perf: 优化操作日志 (#12249)
* perf: 优化操作日志 * perf: 修改migrations中关于Nodes的verbose_name * perf: 优化代码逻辑 * perf: 优化日志详情展示逻辑 * perf: 代码优雅一下 --------- Co-authored-by: jiangweidong <weidong.jiang@fit2cloud.com>pull/12280/head
parent
e193d7a942
commit
8f82ca9856
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {}
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue