2022-11-04 06:22:38 +00:00
|
|
|
|
# ~*~ coding: utf-8 ~*~
|
2023-07-24 03:52:25 +00:00
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
2022-11-04 06:22:38 +00:00
|
|
|
|
|
|
|
|
|
from audits.models import OperateLog
|
2023-12-05 09:26:47 +00:00
|
|
|
|
from perms.const import ActionChoices
|
2022-11-04 06:22:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OperateLogStore(object):
|
2024-06-14 06:25:14 +00:00
|
|
|
|
# 使用 Unicode 单元分隔符\u001f,替代旧的分隔符\0 PostgreSQL 数据库不支持\0
|
|
|
|
|
SEP = '\u001f'
|
|
|
|
|
OLD_SEP = '\0'
|
2023-01-17 04:43:07 +00:00
|
|
|
|
|
2022-11-04 06:22:38 +00:00
|
|
|
|
def __init__(self, config):
|
|
|
|
|
self.model = OperateLog
|
2023-01-17 04:43:07 +00:00
|
|
|
|
self.max_length = 2048
|
2022-11-04 06:22:38 +00:00
|
|
|
|
self.max_length_tip_msg = _(
|
|
|
|
|
'The text content is too long. Use Elasticsearch to store operation logs'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def ping(timeout=None):
|
|
|
|
|
return True
|
|
|
|
|
|
2024-06-14 06:25:14 +00:00
|
|
|
|
@classmethod
|
|
|
|
|
def split_value(cls, value):
|
|
|
|
|
"""
|
|
|
|
|
Attempt to split the string using the new separator.
|
|
|
|
|
If it fails, attempt to split using the old separator.
|
|
|
|
|
If both fail, return the original string and an empty string.
|
|
|
|
|
|
|
|
|
|
:param value: The string to split
|
|
|
|
|
:return: The split parts (before, after)
|
|
|
|
|
"""
|
|
|
|
|
for sep in (cls.SEP, cls.OLD_SEP):
|
|
|
|
|
parts = value.split(sep, 1)
|
|
|
|
|
if len(parts) == 2:
|
|
|
|
|
return parts[0], parts[1]
|
|
|
|
|
return value, ''
|
|
|
|
|
|
2023-01-17 04:43:07 +00:00
|
|
|
|
@classmethod
|
|
|
|
|
def convert_before_after_to_diff(cls, before, after):
|
|
|
|
|
if not isinstance(before, dict):
|
|
|
|
|
before = dict()
|
|
|
|
|
if not isinstance(after, dict):
|
|
|
|
|
after = dict()
|
|
|
|
|
|
|
|
|
|
diff = dict()
|
|
|
|
|
keys = set(before.keys()) | set(after.keys())
|
|
|
|
|
for k in keys:
|
|
|
|
|
before_value = before.get(k, '')
|
|
|
|
|
after_value = after.get(k, '')
|
|
|
|
|
diff[k] = '%s%s%s' % (before_value, cls.SEP, after_value)
|
|
|
|
|
return diff
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def convert_diff_to_before_after(cls, diff):
|
|
|
|
|
before, after = dict(), dict()
|
|
|
|
|
if not diff:
|
|
|
|
|
return before, after
|
|
|
|
|
|
|
|
|
|
for k, v in diff.items():
|
2024-06-14 06:25:14 +00:00
|
|
|
|
before_value, after_value = cls.split_value(v)
|
2023-01-17 04:43:07 +00:00
|
|
|
|
before[k], after[k] = before_value, after_value
|
|
|
|
|
return before, after
|
|
|
|
|
|
2023-12-05 09:26:47 +00:00
|
|
|
|
@staticmethod
|
|
|
|
|
def _get_special_handler(resource_type):
|
|
|
|
|
# 根据资源类型,处理特殊字段
|
|
|
|
|
resource_map = {
|
|
|
|
|
'Asset permission': lambda k, v: ActionChoices.display(int(v)) if k == 'Actions' else v
|
|
|
|
|
}
|
2024-05-31 03:05:35 +00:00
|
|
|
|
return resource_map.get(resource_type, lambda k, v: _(v))
|
2023-12-05 09:26:47 +00:00
|
|
|
|
|
2023-01-17 04:43:07 +00:00
|
|
|
|
@classmethod
|
2023-12-05 09:26:47 +00:00
|
|
|
|
def convert_diff_friendly(cls, op_log):
|
2023-01-17 04:43:07 +00:00
|
|
|
|
diff_list = list()
|
2023-12-05 09:26:47 +00:00
|
|
|
|
handler = cls._get_special_handler(op_log.resource_type)
|
|
|
|
|
for k, v in op_log.diff.items():
|
2024-06-14 06:25:14 +00:00
|
|
|
|
before_value, after_value = cls.split_value(v)
|
2023-01-17 04:43:07 +00:00
|
|
|
|
diff_list.append({
|
2023-02-23 08:20:43 +00:00
|
|
|
|
'field': _(k),
|
2024-06-14 06:25:14 +00:00
|
|
|
|
'before': handler(k, before_value) if before_value else _('empty'),
|
|
|
|
|
'after': handler(k, after_value) if after_value else _('empty'),
|
2023-01-17 04:43:07 +00:00
|
|
|
|
})
|
|
|
|
|
return diff_list
|
|
|
|
|
|
2022-11-04 06:22:38 +00:00
|
|
|
|
def save(self, **kwargs):
|
2023-12-05 09:26:47 +00:00
|
|
|
|
log_id = kwargs.get('id', None)
|
2023-01-17 04:43:07 +00:00
|
|
|
|
before = kwargs.pop('before') or {}
|
|
|
|
|
after = kwargs.pop('after') or {}
|
2022-11-04 06:22:38 +00:00
|
|
|
|
|
|
|
|
|
op_log = self.model.objects.filter(pk=log_id).first()
|
|
|
|
|
if op_log is not None:
|
2023-01-17 04:43:07 +00:00
|
|
|
|
op_log_diff = op_log.diff or {}
|
|
|
|
|
op_before, op_after = self.convert_diff_to_before_after(op_log_diff)
|
|
|
|
|
before.update(op_before)
|
|
|
|
|
after.update(op_after)
|
2022-11-04 06:22:38 +00:00
|
|
|
|
else:
|
2023-02-09 10:53:07 +00:00
|
|
|
|
# 限制长度 128 OperateLog.resource.field.max_length, 避免存储失败
|
2023-02-09 10:37:15 +00:00
|
|
|
|
max_length = 128
|
|
|
|
|
resource = kwargs.get('resource', '')
|
2023-02-09 10:53:07 +00:00
|
|
|
|
if resource and isinstance(resource, str):
|
|
|
|
|
kwargs['resource'] = resource[:max_length]
|
2023-01-16 11:02:09 +00:00
|
|
|
|
op_log = self.model(**kwargs)
|
|
|
|
|
|
2023-01-17 04:43:07 +00:00
|
|
|
|
diff = self.convert_before_after_to_diff(before, after)
|
|
|
|
|
if len(str(diff)) > self.max_length:
|
|
|
|
|
limit = {str(_('Tips')): self.max_length_tip_msg}
|
|
|
|
|
diff = self.convert_before_after_to_diff(limit, limit)
|
|
|
|
|
|
2023-02-17 09:17:25 +00:00
|
|
|
|
setattr(op_log, 'LOCKING_ORG', op_log.org_id)
|
2023-01-17 04:43:07 +00:00
|
|
|
|
op_log.diff = diff
|
2023-01-16 11:02:09 +00:00
|
|
|
|
op_log.save()
|