From a779b37c4458907b61cf896dad4bda437926b328 Mon Sep 17 00:00:00 2001
From: pycook <pycook@126.com>
Date: Tue, 26 Sep 2023 21:18:34 +0800
Subject: [PATCH] feat: ci triggers

---
 cmdb-api/Pipfile                    |   3 +-
 cmdb-api/api/commands/click_cmdb.py |  65 +++---
 cmdb-api/api/lib/cmdb/ci.py         | 174 +++++++++-----
 cmdb-api/api/lib/cmdb/ci_type.py    |   4 +-
 cmdb-api/api/lib/cmdb/history.py    |  19 +-
 cmdb-api/api/lib/notify.py          |  20 +-
 cmdb-api/api/lib/webhook.py         |   6 +-
 cmdb-api/api/models/cmdb.py         |   1 +
 cmdb-api/api/tasks/cmdb.py          |  13 +-
 cmdb-api/requirements.txt           |   5 +-
 cmdb-api/settings.example.py        |   4 +
 cmdb-ui/src/api/employee.js         |   2 +-
 cmdb-ui/yarn.lock                   | 345 +++++++++++++++++++++++++++-
 13 files changed, 550 insertions(+), 111 deletions(-)

diff --git a/cmdb-api/Pipfile b/cmdb-api/Pipfile
index 1639b51..458e424 100644
--- a/cmdb-api/Pipfile
+++ b/cmdb-api/Pipfile
@@ -44,11 +44,12 @@ treelib = "==1.6.1"
 flasgger = "==0.9.5"
 Pillow = "==9.3.0"
 # other
-six = "==1.12.0"
+six = "==1.16.0"
 bs4 = ">=0.0.1"
 toposort = ">=1.5"
 requests = ">=2.22.0"
 requests_oauthlib = "==1.3.1"
+markdownify = "==0.11.6"
 PyJWT = "==2.4.0"
 elasticsearch = "==7.17.9"
 future = "==0.18.3"
diff --git a/cmdb-api/api/commands/click_cmdb.py b/cmdb-api/api/commands/click_cmdb.py
index 6d41d98..a191c00 100644
--- a/cmdb-api/api/commands/click_cmdb.py
+++ b/cmdb-api/api/commands/click_cmdb.py
@@ -15,7 +15,6 @@ import api.lib.cmdb.ci
 from api.extensions import db
 from api.extensions import rd
 from api.lib.cmdb.cache import AttributeCache
-from api.lib.cmdb.ci_type import CITypeTriggerManager
 from api.lib.cmdb.const import PermEnum
 from api.lib.cmdb.const import REDIS_PREFIX_CI
 from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
@@ -24,8 +23,8 @@ from api.lib.cmdb.const import RoleEnum
 from api.lib.cmdb.const import ValueTypeEnum
 from api.lib.exception import AbortException
 from api.lib.perm.acl.acl import ACLManager
+from api.lib.perm.acl.acl import UserCache
 from api.lib.perm.acl.cache import AppCache
-from api.lib.perm.acl.cache import UserCache
 from api.lib.perm.acl.resource import ResourceCRUD
 from api.lib.perm.acl.resource import ResourceTypeCRUD
 from api.lib.perm.acl.role import RoleCRUD
@@ -227,50 +226,60 @@ def cmdb_counter():
 @with_appcontext
 def cmdb_trigger():
     """
-    Trigger execution
+    Trigger execution for date attribute
     """
+    from api.lib.cmdb.ci import CITriggerManager
+
     current_day = datetime.datetime.today().strftime("%Y-%m-%d")
     trigger2cis = dict()
     trigger2completed = dict()
 
     i = 0
     while True:
-        db.session.remove()
-        if datetime.datetime.today().strftime("%Y-%m-%d") != current_day:
-            trigger2cis = dict()
-            trigger2completed = dict()
-            current_day = datetime.datetime.today().strftime("%Y-%m-%d")
+        try:
+            db.session.remove()
 
-        if i == 360 or i == 0:
-            i = 0
-            try:
-                triggers = CITypeTrigger.get_by(to_dict=False)
+            if datetime.datetime.today().strftime("%Y-%m-%d") != current_day:
+                trigger2cis = dict()
+                trigger2completed = dict()
+                current_day = datetime.datetime.today().strftime("%Y-%m-%d")
 
+            if i == 3 or i == 0:
+                i = 0
+                triggers = CITypeTrigger.get_by(to_dict=False, __func_isnot__key_attr_id=None)
                 for trigger in triggers:
-                    ready_cis = CITypeTriggerManager.waiting_cis(trigger)
+                    try:
+                        ready_cis = CITriggerManager.waiting_cis(trigger)
+                    except Exception as e:
+                        print(e)
+                        continue
+
                     if trigger.id not in trigger2cis:
                         trigger2cis[trigger.id] = (trigger, ready_cis)
                     else:
                         cur = trigger2cis[trigger.id]
                         cur_ci_ids = {i.ci_id for i in cur[1]}
-                        trigger2cis[trigger.id] = (trigger, cur[1] + [i for i in ready_cis if i.ci_id not in cur_ci_ids
-                                                                      and i.ci_id not in trigger2completed[trigger.id]])
+                        trigger2cis[trigger.id] = (
+                            trigger, cur[1] + [i for i in ready_cis if i.ci_id not in cur_ci_ids
+                                               and i.ci_id not in trigger2completed.get(trigger.id, {})])
 
-            except Exception as e:
-                print(e)
+            for tid in trigger2cis:
+                trigger, cis = trigger2cis[tid]
+                for ci in copy.deepcopy(cis):
+                    if CITriggerManager.trigger_notify(trigger, ci):
+                        trigger2completed.setdefault(trigger.id, set()).add(ci.ci_id)
 
-        for tid in trigger2cis:
-            trigger, cis = trigger2cis[tid]
-            for ci in copy.deepcopy(cis):
-                if CITypeTriggerManager.trigger_notify(trigger, ci):
-                    trigger2completed.setdefault(trigger.id, set()).add(ci.ci_id)
+                        for _ci in cis:
+                            if _ci.ci_id == ci.ci_id:
+                                cis.remove(_ci)
 
-                    for _ci in cis:
-                        if _ci.ci_id == ci.ci_id:
-                            cis.remove(_ci)
-
-        i += 1
-        time.sleep(10)
+            i += 1
+            time.sleep(10)
+        except Exception as e:
+            import traceback
+            print(traceback.format_exc())
+            current_app.logger.error("cmdb trigger exception: {}".format(e))
+            time.sleep(60)
 
 
 @click.command()
diff --git a/cmdb-api/api/lib/cmdb/ci.py b/cmdb-api/api/lib/cmdb/ci.py
index 39fee78..3f5adf1 100644
--- a/cmdb-api/api/lib/cmdb/ci.py
+++ b/cmdb-api/api/lib/cmdb/ci.py
@@ -54,6 +54,7 @@ from api.models.cmdb import CITypeRelation
 from api.models.cmdb import CITypeTrigger
 from api.tasks.cmdb import ci_cache
 from api.tasks.cmdb import ci_delete
+from api.tasks.cmdb import ci_delete_trigger
 from api.tasks.cmdb import ci_relation_add
 from api.tasks.cmdb import ci_relation_cache
 from api.tasks.cmdb import ci_relation_delete
@@ -464,6 +465,17 @@ class CIManager(object):
         ci_dict = cls.get_cis_by_ids([ci_id])
         ci_dict = ci_dict and ci_dict[0]
 
+        triggers = CITriggerManager.get(ci_dict['_type'])
+        for trigger in triggers:
+            option = trigger['option']
+            if not option.get('enable') or option.get('action') != OperateType.DELETE:
+                continue
+
+            if option.get('filter') and not CITriggerManager.ci_filter(ci_dict.get('_id'), option['filter']):
+                continue
+
+            ci_delete_trigger.apply_async(args=(trigger, OperateType.DELETE, ci_dict), queue=CMDB_QUEUE)
+
         attrs = CITypeAttribute.get_by(type_id=ci.type_id, to_dict=False)
         attr_names = set([AttributeCache.get(attr.attr_id).name for attr in attrs])
         for attr_name in attr_names:
@@ -486,9 +498,9 @@ class CIManager(object):
 
         db.session.commit()
 
-        record_id = AttributeHistoryManger.add(None, ci_id, [(None, OperateType.DELETE, ci_dict, None)], ci.type_id)
+        AttributeHistoryManger.add(None, ci_id, [(None, OperateType.DELETE, ci_dict, None)], ci.type_id)
 
-        ci_delete.apply_async(args=(ci_dict, OperateType.DELETE, record_id), queue=CMDB_QUEUE)
+        ci_delete.apply_async(args=(ci_id,), queue=CMDB_QUEUE)
 
         return ci_id
 
@@ -910,56 +922,89 @@ class CIRelationManager(object):
 class CITriggerManager(object):
     @staticmethod
     def get(type_id):
-        return CITypeTrigger.get_by(type_id=type_id, to_dict=False)
+        db.session.remove()
+        return CITypeTrigger.get_by(type_id=type_id, to_dict=True)
 
     @staticmethod
-    def _exec_webhook(operate_type, webhook, ci_dict, trigger_id, record_id):
-        try:
-            response = webhook_request(webhook, ci_dict).text
+    def _update_old_attr_value(record_id, ci_dict):
+        attr_history = AttributeHistory.get_by(record_id=record_id, to_dict=False)
+        attr_dict = dict()
+        for attr_h in attr_history:
+            attr_dict['old_{}'.format(AttributeCache.get(attr_h.attr_id).name)] = attr_h.old
+
+        ci_dict.update({'old_{}'.format(k): ci_dict[k] for k in ci_dict})
+
+        ci_dict.update(attr_dict)
+
+    @classmethod
+    def _exec_webhook(cls, operate_type, webhook, ci_dict, trigger_id, trigger_name, record_id, ci_id=None, app=None):
+        app = app or current_app
+
+        with app.app_context():
+            if operate_type == OperateType.UPDATE:
+                cls._update_old_attr_value(record_id, ci_dict)
+
+            if ci_id is not None:
+                ci_dict = CIManager().get_ci_by_id_from_db(ci_id, need_children=False, use_master=False)
+
+            try:
+                response = webhook_request(webhook, ci_dict).text
+                is_ok = True
+            except Exception as e:
+                current_app.logger.warning("exec webhook failed: {}".format(e))
+                response = e
+                is_ok = False
+
+            CITriggerHistoryManager.add(operate_type,
+                                        record_id,
+                                        ci_dict.get('_id'),
+                                        trigger_id,
+                                        trigger_name,
+                                        is_ok=is_ok,
+                                        webhook=response)
+
+            return is_ok
+
+    @classmethod
+    def _exec_notify(cls, operate_type, notify, ci_dict, trigger_id, trigger_name, record_id, ci_id=None, app=None):
+        app = app or current_app
+
+        with app.app_context():
+
+            if ci_id is not None:
+                ci_dict = CIManager().get_ci_by_id_from_db(ci_id, need_children=False, use_master=False)
+
+            if operate_type == OperateType.UPDATE:
+                cls._update_old_attr_value(record_id, ci_dict)
+
             is_ok = True
-        except Exception as e:
-            current_app.logger.warning("exec webhook failed: {}".format(e))
-            response = e
-            is_ok = False
+            response = ''
+            for method in (notify.get('method') or []):
+                try:
+                    res = notify_send(notify.get('subject'), notify.get('body'), [method],
+                                      notify.get('tos'), ci_dict)
+                    response = "{}\n{}".format(response, res)
+                except Exception as e:
+                    current_app.logger.warning("send notify failed: {}".format(e))
+                    response = "{}\n{}".format(response, e)
+                    is_ok = False
 
-        CITriggerHistoryManager.add(operate_type,
-                                    record_id,
-                                    ci_dict.get('_id'),
-                                    trigger_id,
-                                    is_ok=is_ok,
-                                    webhook=response)
+            CITriggerHistoryManager.add(operate_type,
+                                        record_id,
+                                        ci_dict.get('_id'),
+                                        trigger_id,
+                                        trigger_name,
+                                        is_ok=is_ok,
+                                        notify=response.strip())
 
-        return is_ok
-
-    @staticmethod
-    def _exec_notify(operate_type, notify, ci_dict, trigger_id, record_id, ci_id=None):
-
-        if ci_id is not None:
-            ci_dict = CIManager().get_ci_by_id_from_db(ci_id, need_children=False, use_master=False)
-
-        try:
-            response = notify_send(notify.get('subject'), notify.get('body'), notify.get('tos'), ci_dict)
-            is_ok = True
-        except Exception as e:
-            current_app.logger.warning("send notify failed: {}".format(e))
-            response = e
-            is_ok = False
-
-        CITriggerHistoryManager.add(operate_type,
-                                    record_id,
-                                    ci_dict.get('_id'),
-                                    trigger_id,
-                                    is_ok=is_ok,
-                                    notify=response)
-
-        return is_ok
+            return is_ok
 
     @staticmethod
     def ci_filter(ci_id, other_filter):
         from api.lib.cmdb.search import SearchError
         from api.lib.cmdb.search.ci import search
 
-        query = "_id:{},{}".format(ci_id, other_filter)
+        query = "{},_id:{}".format(other_filter, ci_id)
 
         try:
             _, _, _, _, numfound, _ = search(query).search()
@@ -973,28 +1018,40 @@ class CITriggerManager(object):
         triggers = cls.get(type_id) or []
 
         for trigger in triggers:
-            if not trigger.option.get('enable'):
+            option = trigger['option']
+            if not option.get('enable'):
                 continue
 
-            if trigger.option.get('filter') and not cls.ci_filter(ci_dict.get('_id'), trigger.option['filter']):
+            if option.get('filter') and not cls.ci_filter(ci_dict.get('_id'), option['filter']):
                 continue
 
-            if trigger.option.get('attr_ids') and isinstance(trigger.option['attr_ids'], list):
-                if not (set(trigger.option['attr_ids']) &
+            if option.get('attr_ids') and isinstance(option['attr_ids'], list):
+                if not (set(option['attr_ids']) &
                         set([i.attr_id for i in AttributeHistory.get_by(record_id=record_id, to_dict=False)])):
                     continue
 
-            if trigger.option.get('action') == operate_type:
-                if trigger.option.get('webhooks'):
-                    cls._exec_webhook(operate_type, trigger.option['webhooks'], ci_dict, trigger.id, record_id)
-                elif trigger.option.get('notifies'):
-                    cls._exec_notify(operate_type, trigger.option['notifies'], ci_dict, trigger.id, record_id)
+            if option.get('action') == operate_type:
+                cls.fire_by_trigger(trigger, operate_type, ci_dict, record_id)
+
+    @classmethod
+    def fire_by_trigger(cls, trigger, operate_type, ci_dict, record_id=None):
+        option = trigger['option']
+
+        if option.get('webhooks'):
+            cls._exec_webhook(operate_type, option['webhooks'], ci_dict, trigger['id'],
+                              option.get('name'), record_id)
+
+        elif option.get('notifies'):
+            cls._exec_notify(operate_type, option['notifies'], ci_dict, trigger['id'],
+                             option.get('name'), record_id)
 
     @classmethod
     def waiting_cis(cls, trigger):
         now = datetime.datetime.today()
 
-        delta_time = datetime.timedelta(days=(trigger.option.get('before_days', 0) or 0))
+        config = trigger.option.get('notifies') or {}
+
+        delta_time = datetime.timedelta(days=(config.get('before_days', 0) or 0))
 
         attr = AttributeCache.get(trigger.attr_id)
 
@@ -1022,10 +1079,17 @@ class CITriggerManager(object):
         :param ci:
         :return:
         """
-        if (trigger.notify.get('notify_at') == datetime.datetime.now().strftime("%H:%M") or
-                not trigger.option.get('notify_at')):
-            threading.Thread(target=cls._exec_notify, args=(
-                None, trigger.option['notifies'], None, trigger.id, None, ci.id)).start()
+        if (trigger.option.get('notifies', {}).get('notify_at') == datetime.datetime.now().strftime("%H:%M") or
+                not trigger.option.get('notifies', {}).get('notify_at')):
+
+            if trigger.option.get('webhooks'):
+                threading.Thread(target=cls._exec_webhook, args=(
+                    None, trigger.option['webhooks'], None, trigger.id, trigger.option.get('name'), None, ci.ci_id,
+                    current_app._get_current_object())).start()
+            elif trigger.option.get('notifies'):
+                threading.Thread(target=cls._exec_notify, args=(
+                    None, trigger.option['notifies'], None, trigger.id, trigger.option.get('name'), None, ci.ci_id,
+                    current_app._get_current_object())).start()
 
             return True
 
diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py
index 38e091b..d787c12 100644
--- a/cmdb-api/api/lib/cmdb/ci_type.py
+++ b/cmdb-api/api/lib/cmdb/ci_type.py
@@ -1187,12 +1187,12 @@ class CITypeTriggerManager(object):
         return trigger.to_dict()
 
     @staticmethod
-    def update(_id, option):
+    def update(_id, attr_id, option):
         existed = (CITypeTrigger.get_by_id(_id) or
                    abort(404, ErrFormat.ci_type_trigger_not_found.format("id={}".format(_id))))
 
         existed2 = existed.to_dict()
-        new = existed.update(option=option)
+        new = existed.update(attr_id=attr_id or None, option=option, filter_none=False)
 
         CITypeHistoryManager.add(CITypeOperateType.UPDATE_TRIGGER,
                                  existed.type_id,
diff --git a/cmdb-api/api/lib/cmdb/history.py b/cmdb-api/api/lib/cmdb/history.py
index bb4537f..6d1f542 100644
--- a/cmdb-api/api/lib/cmdb/history.py
+++ b/cmdb-api/api/lib/cmdb/history.py
@@ -293,13 +293,13 @@ class CITriggerHistoryManager(object):
     @staticmethod
     def get(page, page_size, type_id=None, trigger_id=None, operate_type=None):
         query = CITriggerHistory.get_by(only_query=True)
-        if type_id is not None:
+        if type_id:
             query = query.filter(CITriggerHistory.type_id == type_id)
 
         if trigger_id:
             query = query.filter(CITriggerHistory.trigger_id == trigger_id)
 
-        if operate_type is not None:
+        if operate_type:
             query = query.filter(CITriggerHistory.operate_type == operate_type)
 
         numfound = query.count()
@@ -316,22 +316,22 @@ class CITriggerHistoryManager(object):
 
     @staticmethod
     def get_by_ci_id(ci_id):
-        res = db.session.query(CITriggerHistory, CITypeTrigger, OperationRecord).join(
-            CITypeTrigger, CITypeTrigger.id == CITriggerHistory.trigger_id).join(
-            OperationRecord, OperationRecord.id == CITriggerHistory.record_id).filter(
+        res = db.session.query(CITriggerHistory, CITypeTrigger).join(
+            CITypeTrigger, CITypeTrigger.id == CITriggerHistory.trigger_id).filter(
             CITriggerHistory.ci_id == ci_id).order_by(CITriggerHistory.id.desc())
 
         result = []
         id2trigger = dict()
         for i in res:
             hist = i.CITriggerHistory
-            record = i.OperationRecord
             item = dict(is_ok=hist.is_ok,
                         operate_type=hist.operate_type,
                         notify=hist.notify,
+                        trigger_id=hist.trigger_id,
+                        trigger_name=hist.trigger_name,
                         webhook=hist.webhook,
-                        created_at=record.created_at.strftime('%Y-%m-%d %H:%M:%S'),
-                        record_id=record.id,
+                        created_at=hist.created_at.strftime('%Y-%m-%d %H:%M:%S'),
+                        record_id=hist.record_id,
                         hid=hist.id
                         )
             if i.CITypeTrigger.id not in id2trigger:
@@ -342,12 +342,13 @@ class CITriggerHistoryManager(object):
         return dict(items=result, id2trigger=id2trigger)
 
     @staticmethod
-    def add(operate_type, record_id, ci_id, trigger_id, is_ok=False, notify=None, webhook=None):
+    def add(operate_type, record_id, ci_id, trigger_id, trigger_name, is_ok=False, notify=None, webhook=None):
 
         CITriggerHistory.create(operate_type=operate_type,
                                 record_id=record_id,
                                 ci_id=ci_id,
                                 trigger_id=trigger_id,
+                                trigger_name=trigger_name,
                                 is_ok=is_ok,
                                 notify=notify,
                                 webhook=webhook)
diff --git a/cmdb-api/api/lib/notify.py b/cmdb-api/api/lib/notify.py
index c32087b..93c8e1f 100644
--- a/cmdb-api/api/lib/notify.py
+++ b/cmdb-api/api/lib/notify.py
@@ -5,23 +5,32 @@ import json
 import requests
 from flask import current_app
 from jinja2 import Template
+from markdownify import markdownify as md
 
 from api.lib.mail import send_mail
 
 
-def _request_messenger(subject, body, tos, sender):
+def _request_messenger(subject, body, tos, sender, payload):
     params = dict(sender=sender, title=subject,
                   tos=[to[sender] for to in tos if to.get(sender)])
 
     if not params['tos']:
         raise Exception("no receivers")
 
+    params['tos'] = [Template(i).render(payload) for i in params['tos'] if i.strip()]
+
     if sender == "email":
         params['msgtype'] = 'text/html'
         params['content'] = body
     else:
-        params['msgtype'] = 'text'
-        params['content'] = json.dumps(dict(content=subject or body))
+        params['msgtype'] = 'markdown'
+        try:
+            content = md("{}\n{}".format(subject or '', body or ''))
+        except Exception as e:
+            current_app.logger.warning("html2markdown failed: {}".format(e))
+            content = "{}\n{}".format(subject or '', body or '')
+
+        params['content'] = json.dumps(dict(content=content))
 
     resp = requests.post(current_app.config.get('MESSENGER_URL'), json=params)
     if resp.status_code != 200:
@@ -32,14 +41,15 @@ def _request_messenger(subject, body, tos, sender):
 
 def notify_send(subject, body, methods, tos, payload=None):
     payload = payload or {}
+    payload = {k: v or '' for k, v in payload.items()}
     subject = Template(subject).render(payload)
     body = Template(body).render(payload)
 
     res = ''
     for method in methods:
         if method == "email" and not current_app.config.get('USE_MESSENGER', True):
-            send_mail(None, [to.get('email') for to in tos], subject, body)
+            send_mail(None, [Template(to.get('email')).render(payload) for to in tos], subject, body)
 
-        res += _request_messenger(subject, body, tos, method) + "\n"
+        res += (_request_messenger(subject, body, tos, method, payload) + "\n")
 
     return res
diff --git a/cmdb-api/api/lib/webhook.py b/cmdb-api/api/lib/webhook.py
index 280be18..a5133e0 100644
--- a/cmdb-api/api/lib/webhook.py
+++ b/cmdb-api/api/lib/webhook.py
@@ -82,12 +82,16 @@ def webhook_request(webhook, payload):
     """
     assert webhook.get('url') is not None
 
+    payload = {k: v or '' for k, v in payload.items()}
+
     url = Template(webhook['url']).render(payload)
 
     params = webhook.get('parameters') or None
     if isinstance(params, dict):
         params = json.loads(Template(json.dumps(params)).render(payload))
 
+    headers = json.loads(Template(json.dumps(webhook.get('headers') or {})).render(payload))
+
     data = Template(json.dumps(webhook.get('body', ''))).render(payload)
     auth = _wrap_auth(**webhook.get('authorization', {}))
 
@@ -99,7 +103,7 @@ def webhook_request(webhook, payload):
     return request(
         url,
         params=params,
-        headers=webhook.get('headers') or None,
+        headers=headers or None,
         data=data,
         auth=auth
     )
diff --git a/cmdb-api/api/models/cmdb.py b/cmdb-api/api/models/cmdb.py
index 6f70fec..ccc4084 100644
--- a/cmdb-api/api/models/cmdb.py
+++ b/cmdb-api/api/models/cmdb.py
@@ -139,6 +139,7 @@ class CITriggerHistory(Model):
     record_id = db.Column(db.Integer, db.ForeignKey("c_records.id"))
     ci_id = db.Column(db.Integer, index=True, nullable=False)
     trigger_id = db.Column(db.Integer, db.ForeignKey("c_c_t_t.id"))
+    trigger_name = db.Column(db.String(64))
     is_ok = db.Column(db.Boolean, default=False)
     notify = db.Column(db.Text)
     webhook = db.Column(db.Text)
diff --git a/cmdb-api/api/tasks/cmdb.py b/cmdb-api/api/tasks/cmdb.py
index 34aa315..03e4fd5 100644
--- a/cmdb-api/api/tasks/cmdb.py
+++ b/cmdb-api/api/tasks/cmdb.py
@@ -65,10 +65,7 @@ def batch_ci_cache(ci_ids, ):  # only for attribute change index
 
 
 @celery.task(name="cmdb.ci_delete", queue=CMDB_QUEUE)
-def ci_delete(ci_dict, operate_type, record_id):
-    from api.lib.cmdb.ci import CITriggerManager
-
-    ci_id = ci_dict.get('_id')
+def ci_delete(ci_id):
     current_app.logger.info(ci_id)
 
     if current_app.config.get("USE_ES"):
@@ -78,10 +75,16 @@ def ci_delete(ci_dict, operate_type, record_id):
 
     current_app.logger.info("{0} delete..........".format(ci_id))
 
+
+@celery.task(name="cmdb.ci_delete_trigger", queue=CMDB_QUEUE)
+def ci_delete_trigger(trigger, operate_type, ci_dict):
+    current_app.logger.info('delete ci {} trigger'.format(ci_dict['_id']))
+    from api.lib.cmdb.ci import CITriggerManager
+
     current_app.test_request_context().push()
     login_user(UserCache.get('worker'))
 
-    CITriggerManager.fire(operate_type, ci_dict, record_id)
+    CITriggerManager.fire_by_trigger(trigger, operate_type, ci_dict)
 
 
 @celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
diff --git a/cmdb-api/requirements.txt b/cmdb-api/requirements.txt
index 6e8a788..d98316e 100644
--- a/cmdb-api/requirements.txt
+++ b/cmdb-api/requirements.txt
@@ -37,11 +37,12 @@ PyYAML==6.0
 redis==4.6.0
 requests==2.31.0
 requests_oauthlib==1.3.1
-six==1.12.0
+markdownify==0.11.6
+six==1.16.0
 SQLAlchemy==1.4.49
 supervisor==4.0.3
 timeout-decorator==0.5.0
 toposort==1.10
 treelib==1.6.1
 Werkzeug==2.3.6
-WTForms==3.0.0
\ No newline at end of file
+WTForms==3.0.0
diff --git a/cmdb-api/settings.example.py b/cmdb-api/settings.example.py
index 0102322..26f2d8f 100644
--- a/cmdb-api/settings.example.py
+++ b/cmdb-api/settings.example.py
@@ -94,3 +94,7 @@ ES_HOST = '127.0.0.1'
 USE_ES = False
 
 BOOL_TRUE = ['true', 'TRUE', 'True', True, '1', 1, "Yes", "YES", "yes", 'Y', 'y']
+
+# # messenger
+USE_MESSENGER = True
+MESSENGER_URL = "http://{messenger_url}/v1/message"
diff --git a/cmdb-ui/src/api/employee.js b/cmdb-ui/src/api/employee.js
index 2f2f940..31557cf 100644
--- a/cmdb-ui/src/api/employee.js
+++ b/cmdb-ui/src/api/employee.js
@@ -124,4 +124,4 @@ export function getNoticeByEmployeeIds(data) {
     method: 'post',
     data
   })
-}
\ No newline at end of file
+}
diff --git a/cmdb-ui/yarn.lock b/cmdb-ui/yarn.lock
index 5602f0f..b4b910b 100644
--- a/cmdb-ui/yarn.lock
+++ b/cmdb-ui/yarn.lock
@@ -1214,6 +1214,13 @@
   dependencies:
     regenerator-runtime "^0.13.11"
 
+"@babel/runtime@^7.12.0":
+  version "7.23.1"
+  resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d"
+  integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==
+  dependencies:
+    regenerator-runtime "^0.14.0"
+
 "@babel/template@7.0.0-beta.44":
   version "7.0.0-beta.44"
   resolved "https://mirrors.huaweicloud.com/repository/npm/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
@@ -1723,6 +1730,11 @@
   resolved "https://mirrors.huaweicloud.com/repository/npm/@soda/get-current-script/-/get-current-script-1.0.2.tgz#a53515db25d8038374381b73af20bb4f2e508d87"
   integrity sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==
 
+"@transloadit/prettier-bytes@0.0.7":
+  version "0.0.7"
+  resolved "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz#cdb5399f445fdd606ed833872fa0cabdbc51686b"
+  integrity sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==
+
 "@types/babel__core@^7.1.0":
   version "7.20.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b"
@@ -1805,6 +1817,11 @@
   resolved "https://mirrors.huaweicloud.com/repository/npm/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"
   integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==
 
+"@types/event-emitter@^0.3.3":
+  version "0.3.3"
+  resolved "https://registry.npmmirror.com/@types/event-emitter/-/event-emitter-0.3.3.tgz#727032a9fc67565f96bbd78b2e2809275c97d7e7"
+  integrity sha512-UfnOK1pIxO7P+EgPRZXD9jMpimd8QEFcEZ5R67R1UhGbv4zghU5+NE7U8M8G9H5Jc8FI51rqDWQs6FtUfq2e/Q==
+
 "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33":
   version "4.17.35"
   resolved "https://mirrors.huaweicloud.com/repository/npm/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz#c95dd4424f0d32e525d23812aa8ab8e4d3906c4f"
@@ -2057,6 +2074,49 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@uppy/companion-client@^2.2.2":
+  version "2.2.2"
+  resolved "https://registry.npmmirror.com/@uppy/companion-client/-/companion-client-2.2.2.tgz#c70b42fdcca728ef88b3eebf7ee3e2fa04b4923b"
+  integrity sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==
+  dependencies:
+    "@uppy/utils" "^4.1.2"
+    namespace-emitter "^2.0.1"
+
+"@uppy/core@^2.1.1":
+  version "2.3.4"
+  resolved "https://registry.npmmirror.com/@uppy/core/-/core-2.3.4.tgz#260b85b6bf3aa03cdc67da231f8c69cfbfdcc84a"
+  integrity sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==
+  dependencies:
+    "@transloadit/prettier-bytes" "0.0.7"
+    "@uppy/store-default" "^2.1.1"
+    "@uppy/utils" "^4.1.3"
+    lodash.throttle "^4.1.1"
+    mime-match "^1.0.2"
+    namespace-emitter "^2.0.1"
+    nanoid "^3.1.25"
+    preact "^10.5.13"
+
+"@uppy/store-default@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/@uppy/store-default/-/store-default-2.1.1.tgz#62a656a099bdaa012306e054d093754cb2d36e3e"
+  integrity sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==
+
+"@uppy/utils@^4.1.2", "@uppy/utils@^4.1.3":
+  version "4.1.3"
+  resolved "https://registry.npmmirror.com/@uppy/utils/-/utils-4.1.3.tgz#9d0be6ece4df25f228d30ef40be0f14208258ce3"
+  integrity sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==
+  dependencies:
+    lodash.throttle "^4.1.1"
+
+"@uppy/xhr-upload@^2.0.3":
+  version "2.1.3"
+  resolved "https://registry.npmmirror.com/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz#0d4e355332fe0c6eb372d7731315e04d02aeeb18"
+  integrity sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==
+  dependencies:
+    "@uppy/companion-client" "^2.2.2"
+    "@uppy/utils" "^4.1.2"
+    nanoid "^3.1.25"
+
 "@vue/babel-helper-vue-jsx-merge-props@^1.4.0":
   version "1.4.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2"
@@ -2383,6 +2443,84 @@
   resolved "https://mirrors.huaweicloud.com/repository/npm/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz#b6b40a7625429d2bd7c2281ddba601ed05dc7f1a"
   integrity sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==
 
+"@wangeditor/basic-modules@^1.1.7":
+  version "1.1.7"
+  resolved "https://registry.npmmirror.com/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz#a9c3ccf4ef53332f29550d59d3676e15f395946f"
+  integrity sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==
+  dependencies:
+    is-url "^1.2.4"
+
+"@wangeditor/code-highlight@^1.0.3":
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz#90256857714d5c0cf83ac475aea64db7bf29a7cd"
+  integrity sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==
+  dependencies:
+    prismjs "^1.23.0"
+
+"@wangeditor/core@^1.1.19":
+  version "1.1.19"
+  resolved "https://registry.npmmirror.com/@wangeditor/core/-/core-1.1.19.tgz#f9155f7fd92d03cb1982405b3b82e54c31f1c2b0"
+  integrity sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==
+  dependencies:
+    "@types/event-emitter" "^0.3.3"
+    event-emitter "^0.3.5"
+    html-void-elements "^2.0.0"
+    i18next "^20.4.0"
+    scroll-into-view-if-needed "^2.2.28"
+    slate-history "^0.66.0"
+
+"@wangeditor/editor-for-vue@^1.0.0":
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/@wangeditor/editor-for-vue/-/editor-for-vue-1.0.2.tgz#62674d56354319ff8dcc83db5c62cec4437ee906"
+  integrity sha512-BOENvAXJVtVXlE2X50AAvjV82YlCUeu5cbeR0cvEQHQjYtiVnJtq7HSoj85r2kTgGouI5OrpJG9BBEjSjUSPyA==
+
+"@wangeditor/editor@^5.1.23":
+  version "5.1.23"
+  resolved "https://registry.npmmirror.com/@wangeditor/editor/-/editor-5.1.23.tgz#c9d2007b7cb0ceef6b72692b4ee87b01ee2367b3"
+  integrity sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==
+  dependencies:
+    "@uppy/core" "^2.1.1"
+    "@uppy/xhr-upload" "^2.0.3"
+    "@wangeditor/basic-modules" "^1.1.7"
+    "@wangeditor/code-highlight" "^1.0.3"
+    "@wangeditor/core" "^1.1.19"
+    "@wangeditor/list-module" "^1.0.5"
+    "@wangeditor/table-module" "^1.1.4"
+    "@wangeditor/upload-image-module" "^1.0.2"
+    "@wangeditor/video-module" "^1.1.4"
+    dom7 "^3.0.0"
+    is-hotkey "^0.2.0"
+    lodash.camelcase "^4.3.0"
+    lodash.clonedeep "^4.5.0"
+    lodash.debounce "^4.0.8"
+    lodash.foreach "^4.5.0"
+    lodash.isequal "^4.5.0"
+    lodash.throttle "^4.1.1"
+    lodash.toarray "^4.4.0"
+    nanoid "^3.2.0"
+    slate "^0.72.0"
+    snabbdom "^3.1.0"
+
+"@wangeditor/list-module@^1.0.5":
+  version "1.0.5"
+  resolved "https://registry.npmmirror.com/@wangeditor/list-module/-/list-module-1.0.5.tgz#3fc0b167acddf885536b45fa0c127f9c6adaea33"
+  integrity sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==
+
+"@wangeditor/table-module@^1.1.4":
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/@wangeditor/table-module/-/table-module-1.1.4.tgz#757d4a5868b2b658041cd323854a4d707c8347e9"
+  integrity sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==
+
+"@wangeditor/upload-image-module@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz#89e9b9467e10cbc6b11dc5748e08dd23aaebee30"
+  integrity sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==
+
+"@wangeditor/video-module@^1.1.4":
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/@wangeditor/video-module/-/video-module-1.1.4.tgz#b9df1b3ab2cd53f678b19b4d927e200774a6f532"
+  integrity sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==
+
 "@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5":
   version "1.11.6"
   resolved "https://mirrors.huaweicloud.com/repository/npm/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24"
@@ -4398,6 +4536,11 @@ compression@^1.7.4:
     safe-buffer "5.1.2"
     vary "~1.1.2"
 
+compute-scroll-into-view@^1.0.20:
+  version "1.0.20"
+  resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43"
+  integrity sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==
+
 concat-map@0.0.1:
   version "0.0.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -5060,6 +5203,14 @@ d3-voronoi@^1.1.2:
   resolved "https://mirrors.huaweicloud.com/repository/npm/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297"
   integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==
 
+d@1, d@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
+  integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==
+  dependencies:
+    es5-ext "^0.10.50"
+    type "^1.0.1"
+
 dagre@^0.8.2, dagre@~0.8.5:
   version "0.8.5"
   resolved "https://mirrors.huaweicloud.com/repository/npm/dagre/-/dagre-0.8.5.tgz#ba30b0055dac12b6c1fcc247817442777d06afee"
@@ -5401,6 +5552,13 @@ dom-to-image@~2.6.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/dom-to-image/-/dom-to-image-2.6.0.tgz#8a503608088c87b1c22f9034ae032e1898955867"
   integrity sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==
 
+dom7@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz#b861ce5d67a6becd7aaa3ad02942ff14b1240331"
+  integrity sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==
+  dependencies:
+    ssr-window "^3.0.0-alpha.1"
+
 domain-browser@^1.1.1:
   version "1.2.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
@@ -5743,6 +5901,32 @@ es-to-primitive@^1.2.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.2"
 
+es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@~0.10.14:
+  version "0.10.62"
+  resolved "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
+  integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
+  dependencies:
+    es6-iterator "^2.0.3"
+    es6-symbol "^3.1.3"
+    next-tick "^1.1.0"
+
+es6-iterator@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
+  integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
+  dependencies:
+    d "1"
+    es5-ext "^0.10.35"
+    es6-symbol "^3.1.1"
+
+es6-symbol@^3.1.1, es6-symbol@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18"
+  integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==
+  dependencies:
+    d "^1.0.1"
+    ext "^1.1.2"
+
 escalade@^3.1.1:
   version "3.1.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -6007,6 +6191,14 @@ etag@~1.8.1:
   resolved "https://mirrors.huaweicloud.com/repository/npm/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
   integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
 
+event-emitter@^0.3.5:
+  version "0.3.5"
+  resolved "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
+  integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
 event-pubsub@4.3.0:
   version "4.3.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e"
@@ -6183,6 +6375,13 @@ express@^4.16.3, express@^4.17.1:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
+ext@^1.1.2:
+  version "1.7.0"
+  resolved "https://registry.npmmirror.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f"
+  integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
+  dependencies:
+    type "^2.7.2"
+
 extend-shallow@^2.0.1:
   version "2.0.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
@@ -7090,6 +7289,11 @@ html-tags@^3.3.1:
   resolved "https://mirrors.huaweicloud.com/repository/npm/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce"
   integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==
 
+html-void-elements@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-2.0.1.tgz#29459b8b05c200b6c5ee98743c41b979d577549f"
+  integrity sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==
+
 html-webpack-plugin@^3.2.0:
   version "3.2.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz#b01abbd723acaaa7b37b6af4492ebda03d9dd37b"
@@ -7213,6 +7417,13 @@ human-signals@^1.1.1:
   resolved "https://mirrors.huaweicloud.com/repository/npm/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
   integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
 
+i18next@^20.4.0:
+  version "20.6.1"
+  resolved "https://registry.npmmirror.com/i18next/-/i18next-20.6.1.tgz#535e5f6e5baeb685c7d25df70db63bf3cc0aa345"
+  integrity sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==
+  dependencies:
+    "@babel/runtime" "^7.12.0"
+
 iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24:
   version "0.4.24"
   resolved "https://mirrors.huaweicloud.com/repository/npm/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -7262,6 +7473,11 @@ immediate@~3.0.5:
   resolved "https://mirrors.huaweicloud.com/repository/npm/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
   integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
 
+immer@^9.0.6:
+  version "9.0.21"
+  resolved "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
+  integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
+
 import-cwd@^2.0.0:
   version "2.1.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
@@ -7687,6 +7903,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
   dependencies:
     is-extglob "^2.1.1"
 
+is-hotkey@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.npmmirror.com/is-hotkey/-/is-hotkey-0.2.0.tgz#1835a68171a91e5c9460869d96336947c8340cef"
+  integrity sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==
+
 is-mobile@^2.2.1:
   version "2.2.2"
   resolved "https://mirrors.huaweicloud.com/repository/npm/is-mobile/-/is-mobile-2.2.2.tgz#f6c9c5d50ee01254ce05e739bdd835f1ed4e9954"
@@ -7769,6 +7990,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4:
   dependencies:
     isobject "^3.0.1"
 
+is-plain-object@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
+  integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
+
 is-posix-bracket@^0.1.0:
   version "0.1.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
@@ -7844,6 +8070,11 @@ is-typedarray@~1.0.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
   integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
 
+is-url@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.npmmirror.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
+  integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
+
 is-utf8@^0.2.0:
   version "0.2.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
@@ -8842,6 +9073,16 @@ locate-path@^5.0.0:
   dependencies:
     p-locate "^4.1.0"
 
+lodash.camelcase@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+  integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
+
+lodash.clonedeep@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+  integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
 lodash.debounce@^4.0.8:
   version "4.0.8"
   resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@@ -8872,6 +9113,11 @@ lodash.flatten@^4.4.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
   integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==
 
+lodash.foreach@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
+  integrity sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==
+
 lodash.get@^4.4.2:
   version "4.4.2"
   resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@@ -8937,6 +9183,16 @@ lodash.sortby@^4.7.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
   integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==
 
+lodash.throttle@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
+  integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
+
+lodash.toarray@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.npmmirror.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
+  integrity sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==
+
 lodash.transform@^4.6.0:
   version "4.6.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0"
@@ -9185,6 +9441,13 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
   resolved "https://mirrors.huaweicloud.com/repository/npm/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
   integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
 
+mime-match@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/mime-match/-/mime-match-1.0.2.tgz#3f87c31e9af1a5fd485fb9db134428b23bbb7ba8"
+  integrity sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==
+  dependencies:
+    wildcard "^1.1.0"
+
 mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34:
   version "2.1.35"
   resolved "https://mirrors.huaweicloud.com/repository/npm/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
@@ -9389,14 +9652,19 @@ mz@^2.4.0:
     object-assign "^4.0.1"
     thenify-all "^1.0.0"
 
+namespace-emitter@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/namespace-emitter/-/namespace-emitter-2.0.1.tgz#978d51361c61313b4e6b8cf6f3853d08dfa2b17c"
+  integrity sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==
+
 nan@^2.12.1:
   version "2.17.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
   integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
 
-nanoid@^3.3.6:
+nanoid@^3.1.25, nanoid@^3.2.0, nanoid@^3.3.6:
   version "3.3.6"
-  resolved "https://mirrors.huaweicloud.com/repository/npm/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
+  resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
   integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
 
 nanomatch@^1.2.9:
@@ -9441,6 +9709,11 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2:
   resolved "https://mirrors.huaweicloud.com/repository/npm/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
   integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
 
+next-tick@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
+  integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
+
 nice-try@^1.0.4:
   version "1.0.5"
   resolved "https://mirrors.huaweicloud.com/repository/npm/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
@@ -10555,6 +10828,11 @@ postcss@^8.4.14:
     picocolors "^1.0.0"
     source-map-js "^1.0.2"
 
+preact@^10.5.13:
+  version "10.17.1"
+  resolved "https://registry.npmmirror.com/preact/-/preact-10.17.1.tgz#0a1b3c658c019e759326b9648c62912cf5c2dde1"
+  integrity sha512-X9BODrvQ4Ekwv9GURm9AKAGaomqXmip7NQTZgY7gcNmr7XE83adOMJvd3N42id1tMFU7ojiynRsYnY6/BRFxLA==
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://mirrors.huaweicloud.com/repository/npm/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -10607,6 +10885,11 @@ printj@~1.1.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
   integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
 
+prismjs@^1.23.0:
+  version "1.29.0"
+  resolved "https://registry.npmmirror.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12"
+  integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==
+
 process-nextick-args@~2.0.0:
   version "2.0.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -10921,6 +11204,11 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.4:
   resolved "https://mirrors.huaweicloud.com/repository/npm/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
   integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
 
+regenerator-runtime@^0.14.0:
+  version "0.14.0"
+  resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
+  integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
+
 regenerator-transform@^0.15.1:
   version "0.15.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56"
@@ -11355,6 +11643,13 @@ screenfull@^4.2.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/screenfull/-/screenfull-4.2.1.tgz#3245b7bc73d2b7c9a15bd8caaf6965db7cbc7f04"
   integrity sha512-PLSp6f5XdhvjCCCO8OjavRfzkSGL3Qmdm7P82bxyU8HDDDBhDV3UckRaYcRa/NDNTYt8YBpzjoLWHUAejmOjLg==
 
+scroll-into-view-if-needed@^2.2.28:
+  version "2.2.31"
+  resolved "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz#d3c482959dc483e37962d1521254e3295d0d1587"
+  integrity sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==
+  dependencies:
+    compute-scroll-into-view "^1.0.20"
+
 select-hose@^2.0.0:
   version "2.0.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -11571,6 +11866,22 @@ slash@^3.0.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
+slate-history@^0.66.0:
+  version "0.66.0"
+  resolved "https://registry.npmmirror.com/slate-history/-/slate-history-0.66.0.tgz#ac63fddb903098ceb4c944433e3f75fe63acf940"
+  integrity sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==
+  dependencies:
+    is-plain-object "^5.0.0"
+
+slate@^0.72.0:
+  version "0.72.8"
+  resolved "https://registry.npmmirror.com/slate/-/slate-0.72.8.tgz#5a018edf24e45448655293a68bfbcf563aa5ba81"
+  integrity sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==
+  dependencies:
+    immer "^9.0.6"
+    is-plain-object "^5.0.0"
+    tiny-warning "^1.0.3"
+
 slice-ansi@^2.1.0:
   version "2.1.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636"
@@ -11580,6 +11891,11 @@ slice-ansi@^2.1.0:
     astral-regex "^1.0.0"
     is-fullwidth-code-point "^2.0.0"
 
+snabbdom@^3.1.0:
+  version "3.5.1"
+  resolved "https://registry.npmmirror.com/snabbdom/-/snabbdom-3.5.1.tgz#25f80ef15b194baea703d9d5441892e369de18e1"
+  integrity sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==
+
 snapdragon-node@^2.0.1:
   version "2.1.1"
   resolved "https://mirrors.huaweicloud.com/repository/npm/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
@@ -11800,6 +12116,11 @@ sshpk@^1.7.0:
     safer-buffer "^2.0.2"
     tweetnacl "~0.14.0"
 
+ssr-window@^3.0.0-alpha.1:
+  version "3.0.0"
+  resolved "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz#fd5b82801638943e0cc704c4691801435af7ac37"
+  integrity sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==
+
 ssri@^6.0.1:
   version "6.0.2"
   resolved "https://mirrors.huaweicloud.com/repository/npm/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5"
@@ -12313,6 +12634,11 @@ tiny-emitter@^2.0.0:
   resolved "https://mirrors.huaweicloud.com/repository/npm/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
   integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
 
+tiny-warning@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
+  integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+
 tinycolor2@^1.4.1:
   version "1.6.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
@@ -12543,6 +12869,16 @@ type-is@~1.6.18:
     media-typer "0.3.0"
     mime-types "~2.1.24"
 
+type@^1.0.1:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
+  integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
+
+type@^2.7.2:
+  version "2.7.2"
+  resolved "https://registry.npmmirror.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0"
+  integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==
+
 typed-array-length@^1.0.4:
   version "1.0.4"
   resolved "https://mirrors.huaweicloud.com/repository/npm/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
@@ -13413,6 +13749,11 @@ which@^2.0.1:
   dependencies:
     isexe "^2.0.0"
 
+wildcard@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/wildcard/-/wildcard-1.1.2.tgz#a7020453084d8cd2efe70ba9d3696263de1710a5"
+  integrity sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==
+
 window-size@0.1.0:
   version "0.1.0"
   resolved "https://mirrors.huaweicloud.com/repository/npm/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"