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(
|
migrations.AddField(
|
||||||
model_name='asset',
|
model_name='asset',
|
||||||
name='nodes',
|
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(
|
migrations.AddField(
|
||||||
model_name='systemuser',
|
model_name='systemuser',
|
||||||
|
|
|
@ -50,7 +50,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='asset',
|
model_name='asset',
|
||||||
name='nodes',
|
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(
|
migrations.AddField(
|
||||||
model_name='systemuser',
|
model_name='systemuser',
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Migration(migrations.Migration):
|
||||||
('type', models.CharField(max_length=16, verbose_name='Type')),
|
('type', models.CharField(max_length=16, verbose_name='Type')),
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
|
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
|
||||||
('assets', models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets')),
|
('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={
|
options={
|
||||||
'verbose_name': 'Automation task',
|
'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',
|
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets',
|
||||||
verbose_name=_("Domain"), on_delete=models.SET_NULL)
|
verbose_name=_("Domain"), on_delete=models.SET_NULL)
|
||||||
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets',
|
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'))
|
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
||||||
gathered_info = models.JSONField(verbose_name=_('Gathered info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息
|
gathered_info = models.JSONField(verbose_name=_('Gathered info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息
|
||||||
custom_info = models.JSONField(verbose_name=_('Custom info'), default=dict)
|
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):
|
class BaseAutomation(PeriodTaskModelMixin, JMSOrgBaseModel):
|
||||||
accounts = models.JSONField(default=list, verbose_name=_("Accounts"))
|
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"))
|
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
|
||||||
type = models.CharField(max_length=16, verbose_name=_('Type'))
|
type = models.CharField(max_length=16, verbose_name=_('Type'))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
||||||
|
|
|
@ -29,7 +29,7 @@ class CommandFilter(OrgModelMixin):
|
||||||
)
|
)
|
||||||
nodes = models.ManyToManyField(
|
nodes = models.ManyToManyField(
|
||||||
'assets.Node', related_name='cmd_filters', blank=True,
|
'assets.Node', related_name='cmd_filters', blank=True,
|
||||||
verbose_name=_("Nodes")
|
verbose_name=_("Node")
|
||||||
)
|
)
|
||||||
assets = models.ManyToManyField(
|
assets = models.ManyToManyField(
|
||||||
'assets.Asset', related_name='cmd_filters', blank=True,
|
'assets.Asset', related_name='cmd_filters', blank=True,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from audits.models import OperateLog
|
from audits.models import OperateLog
|
||||||
|
from perms.const import ActionChoices
|
||||||
|
|
||||||
|
|
||||||
class OperateLogStore(object):
|
class OperateLogStore(object):
|
||||||
|
@ -45,20 +46,29 @@ class OperateLogStore(object):
|
||||||
before[k], after[k] = before_value, after_value
|
before[k], after[k] = before_value, after_value
|
||||||
return before, after
|
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
|
@classmethod
|
||||||
def convert_diff_friendly(cls, raw_diff):
|
def convert_diff_friendly(cls, op_log):
|
||||||
diff_list = list()
|
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)
|
before, after = v.split(cls.SEP, 1)
|
||||||
diff_list.append({
|
diff_list.append({
|
||||||
'field': _(k),
|
'field': _(k),
|
||||||
'before': before if before else _('empty'),
|
'before': handler(k, before) if before else _('empty'),
|
||||||
'after': after if after else _('empty'),
|
'after': handler(k, after) if after else _('empty'),
|
||||||
})
|
})
|
||||||
return diff_list
|
return diff_list
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
log_id = kwargs.pop('id', None)
|
log_id = kwargs.get('id', None)
|
||||||
before = kwargs.pop('before') or {}
|
before = kwargs.pop('before') or {}
|
||||||
after = kwargs.pop('after') or {}
|
after = kwargs.pop('after') or {}
|
||||||
|
|
||||||
|
|
|
@ -77,10 +77,7 @@ class OperateLogActionDetailSerializer(serializers.ModelSerializer):
|
||||||
fields = ('diff',)
|
fields = ('diff',)
|
||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
data = super().to_representation(instance)
|
return {'diff': OperateLogStore.convert_diff_friendly(instance)}
|
||||||
diff = OperateLogStore.convert_diff_friendly(data['diff'])
|
|
||||||
data['diff'] = diff
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class OperateLogSerializer(BulkOrgResourceModelSerializer):
|
class OperateLogSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
|
|
@ -33,7 +33,9 @@ def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs):
|
||||||
|
|
||||||
with translation.override('en'):
|
with translation.override('en'):
|
||||||
resource_type = instance._meta.verbose_name
|
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')
|
instance_id = current_instance.get('id')
|
||||||
log_id, before_instance = get_instance_dict_from_cache(instance_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',
|
'PermedAsset', 'PermedAccount', 'MenuPermission',
|
||||||
'Permission', 'TicketSession', 'ApplyLoginTicket',
|
'Permission', 'TicketSession', 'ApplyLoginTicket',
|
||||||
'ApplyCommandTicket', 'ApplyLoginAssetTicket',
|
'ApplyCommandTicket', 'ApplyLoginAssetTicket',
|
||||||
'FavoriteAsset',
|
'FavoriteAsset', 'Asset'
|
||||||
}
|
}
|
||||||
for i, app in enumerate(apps.get_models(), 1):
|
for i, app in enumerate(apps.get_models(), 1):
|
||||||
app_name = app._meta.app_label
|
app_name = app._meta.app_label
|
||||||
|
|
|
@ -78,31 +78,37 @@ def _get_instance_field_value(
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def model_to_dict_for_operate_log(instance, include_model_fields=True, include_related_fields=False):
|
def model_to_dict_for_operate_log(
|
||||||
model_need_continue_fields = ['date_updated']
|
instance, include_model_fields=True, include_related_fields=None
|
||||||
m2m_need_continue_fields = ['history_passwords']
|
):
|
||||||
|
def get_related_values(f):
|
||||||
data = _get_instance_field_value(
|
|
||||||
instance, include_model_fields, model_need_continue_fields
|
|
||||||
)
|
|
||||||
|
|
||||||
if include_related_fields:
|
|
||||||
opts = instance._meta
|
|
||||||
for f in opts.many_to_many:
|
|
||||||
value = []
|
value = []
|
||||||
if instance.pk is not None:
|
if instance.pk is not None:
|
||||||
related_name = getattr(f, 'attname', '') or getattr(f, 'related_name', '')
|
related_name = getattr(f, 'attname', '') or getattr(f, 'related_name', '')
|
||||||
if not related_name or related_name in m2m_need_continue_fields:
|
if not related_name or related_name in ['history_passwords']:
|
||||||
continue
|
return
|
||||||
try:
|
try:
|
||||||
value = [str(i) for i in getattr(instance, related_name).all()]
|
value = [str(i) for i in getattr(instance, related_name).all()]
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
if not value:
|
if not value:
|
||||||
continue
|
return
|
||||||
try:
|
try:
|
||||||
field_key = getattr(f, 'verbose_name', None) or f.related_model._meta.verbose_name
|
field_key = getattr(f, 'verbose_name', None) or f.related_model._meta.verbose_name
|
||||||
data.setdefault(str(field_key), value)
|
data.setdefault(str(field_key), value)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
data = _get_instance_field_value(
|
||||||
|
instance, include_model_fields, ['date_updated']
|
||||||
|
)
|
||||||
|
|
||||||
|
if include_related_fields:
|
||||||
|
opts = instance._meta
|
||||||
|
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
|
||||||
|
get_related_values(f)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -584,7 +584,7 @@ class Config(dict):
|
||||||
'APPLET_DOWNLOAD_HOST': '',
|
'APPLET_DOWNLOAD_HOST': '',
|
||||||
|
|
||||||
# FTP 文件上传下载备份阈值,单位(M),当值小于等于0时,不备份
|
# FTP 文件上传下载备份阈值,单位(M),当值小于等于0时,不备份
|
||||||
'FTP_FILE_MAX_STORE': 100,
|
'FTP_FILE_MAX_STORE': 0,
|
||||||
|
|
||||||
# API 分页
|
# API 分页
|
||||||
'MAX_LIMIT_PER_PAGE': 10000,
|
'MAX_LIMIT_PER_PAGE': 10000,
|
||||||
|
|
|
@ -80,7 +80,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='assetpermission',
|
model_name='assetpermission',
|
||||||
name='nodes',
|
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(
|
# migrations.RunPython(
|
||||||
# code=migrate_node_permissions,
|
# code=migrate_node_permissions,
|
||||||
|
|
|
@ -69,7 +69,7 @@ class AssetPermission(LabeledMixin, JMSOrgBaseModel):
|
||||||
'assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset")
|
'assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset")
|
||||||
)
|
)
|
||||||
nodes = models.ManyToManyField(
|
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 默认包含,将来在全局设置中进行控制.
|
# 特殊的账号: @ALL, @INPUT @USER 默认包含,将来在全局设置中进行控制.
|
||||||
accounts = models.JSONField(default=list, verbose_name=_("Account"))
|
accounts = models.JSONField(default=list, verbose_name=_("Account"))
|
||||||
|
|
|
@ -20,3 +20,6 @@ class Task(JMSBaseModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "terminal_task"
|
db_table = "terminal_task"
|
||||||
verbose_name = _("Task")
|
verbose_name = _("Task")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(dict(SessionTaskChoices.choices).get(self.name))
|
||||||
|
|
Loading…
Reference in New Issue