Compare commits

..

1 Commits

Author SHA1 Message Date
pycook
07eb902583 feat(api): my preference support grouping 2024-05-18 22:54:07 +08:00
53 changed files with 2037 additions and 6095 deletions

6
.env
View File

@@ -1,6 +0,0 @@
MYSQL_ROOT_PASSWORD='123456'
MYSQL_HOST='mysql'
MYSQL_PORT=3306
MYSQL_USER='cmdb'
MYSQL_DATABASE='cmdb'
MYSQL_PASSWORD='123456'

View File

@@ -36,7 +36,7 @@ Flask-Caching = ">=1.0.0"
environs = "==4.2.0"
marshmallow = "==2.20.2"
# async tasks
celery = "==5.3.1"
celery = ">=5.3.1"
celery_once = "==3.0.1"
more-itertools = "==5.0.0"
kombu = ">=5.3.1"

View File

@@ -128,7 +128,7 @@ def cmdb_init_acl():
perms = [PermEnum.READ]
elif resource_type == ResourceTypeEnum.CI_TYPE_RELATION:
perms = [PermEnum.ADD, PermEnum.DELETE, PermEnum.GRANT]
elif resource_type in (ResourceTypeEnum.RELATION_VIEW, ResourceTypeEnum.TOPOLOGY_VIEW):
elif resource_type == ResourceTypeEnum.RELATION_VIEW:
perms = [PermEnum.READ, PermEnum.UPDATE, PermEnum.DELETE, PermEnum.GRANT]
ResourceTypeCRUD.add(app_id, resource_type, '', perms)
@@ -326,7 +326,7 @@ def cmdb_inner_secrets_init(address):
"""
init inner secrets for password feature
"""
res, ok = KeyManage(backend=InnerKVManger()).init()
res, ok = KeyManage(backend=InnerKVManger).init()
if not ok:
if res.get("status") == "failed":
KeyManage.print_response(res)

View File

@@ -1090,18 +1090,6 @@ class CIRelationManager(object):
return ci_ids, level2ids
@classmethod
def get_parent_ids(cls, ci_ids):
cis = db.session.query(CIRelation.first_ci_id, CIRelation.second_ci_id, CI.type_id).join(
CI, CI.id == CIRelation.first_ci_id).filter(
CIRelation.second_ci_id.in_(ci_ids)).filter(CIRelation.deleted.is_(False))
result = {}
for ci in cis:
result.setdefault(ci.second_ci_id, []).append((ci.first_ci_id, ci.type_id))
return result
@staticmethod
def _check_constraint(first_ci_id, first_type_id, second_ci_id, second_type_id, type_relation):
db.session.commit()
@@ -1274,83 +1262,52 @@ class CIRelationManager(object):
def build_by_attribute(cls, ci_dict):
type_id = ci_dict['_type']
child_items = CITypeRelation.get_by(parent_id=type_id, only_query=True).filter(
CITypeRelation.parent_attr_ids.isnot(None))
CITypeRelation.parent_attr_id.isnot(None))
for item in child_items:
relations = None
for parent_attr_id, child_attr_id in zip(item.parent_attr_ids, item.child_attr_ids):
_relations = set()
parent_attr = AttributeCache.get(parent_attr_id)
child_attr = AttributeCache.get(child_attr_id)
attr_value = ci_dict.get(parent_attr.name)
value_table = TableMap(attr=child_attr).table
for child in value_table.get_by(attr_id=child_attr.id, value=attr_value, only_query=True).join(
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.child_id):
_relations.add((ci_dict['_id'], child.ci_id))
if relations is None:
relations = _relations
else:
relations &= _relations
for parent_ci_id, child_ci_id in relations:
CIRelationManager.add(parent_ci_id, child_ci_id, valid=False)
parent_attr = AttributeCache.get(item.parent_attr_id)
child_attr = AttributeCache.get(item.child_attr_id)
attr_value = ci_dict.get(parent_attr.name)
value_table = TableMap(attr=child_attr).table
for child in value_table.get_by(attr_id=child_attr.id, value=attr_value, only_query=True).join(
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.child_id):
CIRelationManager.add(ci_dict['_id'], child.ci_id, valid=False)
parent_items = CITypeRelation.get_by(child_id=type_id, only_query=True).filter(
CITypeRelation.child_attr_ids.isnot(None))
CITypeRelation.child_attr_id.isnot(None))
for item in parent_items:
relations = None
for parent_attr_id, child_attr_id in zip(item.parent_attr_ids, item.child_attr_ids):
_relations = set()
parent_attr = AttributeCache.get(parent_attr_id)
child_attr = AttributeCache.get(child_attr_id)
attr_value = ci_dict.get(child_attr.name)
value_table = TableMap(attr=parent_attr).table
for parent in value_table.get_by(attr_id=parent_attr.id, value=attr_value, only_query=True).join(
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.parent_id):
_relations.add((parent.ci_id, ci_dict['_id']))
if relations is None:
relations = _relations
else:
relations &= _relations
for parent_ci_id, child_ci_id in relations:
CIRelationManager.add(parent_ci_id, child_ci_id, valid=False)
parent_attr = AttributeCache.get(item.parent_attr_id)
child_attr = AttributeCache.get(item.child_attr_id)
attr_value = ci_dict.get(child_attr.name)
value_table = TableMap(attr=parent_attr).table
for parent in value_table.get_by(attr_id=parent_attr.id, value=attr_value, only_query=True).join(
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.parent_id):
CIRelationManager.add(parent.ci_id, ci_dict['_id'], valid=False)
@classmethod
def rebuild_all_by_attribute(cls, ci_type_relation):
relations = None
for parent_attr_id, child_attr_id in zip(ci_type_relation['parent_attr_ids'] or [],
ci_type_relation['child_attr_ids'] or []):
parent_attr = AttributeCache.get(ci_type_relation['parent_attr_id'])
child_attr = AttributeCache.get(ci_type_relation['child_attr_id'])
if not parent_attr or not child_attr:
return
_relations = set()
parent_attr = AttributeCache.get(parent_attr_id)
child_attr = AttributeCache.get(child_attr_id)
if not parent_attr or not child_attr:
continue
parent_value_table = TableMap(attr=parent_attr).table
child_value_table = TableMap(attr=child_attr).table
parent_value_table = TableMap(attr=parent_attr).table
child_value_table = TableMap(attr=child_attr).table
parent_values = parent_value_table.get_by(attr_id=parent_attr.id, only_query=True).join(
CI, CI.id == parent_value_table.ci_id).filter(CI.type_id == ci_type_relation['parent_id'])
child_values = child_value_table.get_by(attr_id=child_attr.id, only_query=True).join(
CI, CI.id == child_value_table.ci_id).filter(CI.type_id == ci_type_relation['child_id'])
parent_values = parent_value_table.get_by(attr_id=parent_attr.id, only_query=True).join(
CI, CI.id == parent_value_table.ci_id).filter(CI.type_id == ci_type_relation['parent_id'])
child_values = child_value_table.get_by(attr_id=child_attr.id, only_query=True).join(
CI, CI.id == child_value_table.ci_id).filter(CI.type_id == ci_type_relation['child_id'])
child_value2ci_ids = {}
for child in child_values:
child_value2ci_ids.setdefault(child.value, []).append(child.ci_id)
child_value2ci_ids = {}
for child in child_values:
child_value2ci_ids.setdefault(child.value, []).append(child.ci_id)
for parent in parent_values:
for child_ci_id in child_value2ci_ids.get(parent.value, []):
_relations.add((parent.ci_id, child_ci_id))
if relations is None:
relations = _relations
else:
relations &= _relations
for parent_ci_id, child_ci_id in (relations or []):
try:
cls.add(parent_ci_id, child_ci_id, valid=False)
except:
pass
for parent in parent_values:
for child_ci_id in child_value2ci_ids.get(parent.value, []):
try:
cls.add(parent.ci_id, child_ci_id, valid=False)
except:
pass
class CITriggerManager(object):

View File

@@ -35,7 +35,6 @@ from api.lib.perm.acl.acl import validate_permission
from api.models.cmdb import Attribute
from api.models.cmdb import AutoDiscoveryCI
from api.models.cmdb import AutoDiscoveryCIType
from api.models.cmdb import AutoDiscoveryCITypeRelation
from api.models.cmdb import CI
from api.models.cmdb import CIFilterPerms
from api.models.cmdb import CIType
@@ -55,7 +54,6 @@ from api.models.cmdb import PreferenceSearchOption
from api.models.cmdb import PreferenceShowAttributes
from api.models.cmdb import PreferenceTreeView
from api.models.cmdb import RelationType
from api.models.cmdb import TopologyView
class CITypeManager(object):
@@ -252,12 +250,6 @@ class CITypeManager(object):
for item in AutoDiscoveryCI.get_by(type_id=type_id, to_dict=False):
item.delete(commit=False)
for item in AutoDiscoveryCITypeRelation.get_by(ad_type_id=type_id, to_dict=False):
item.delete(commit=False)
for item in AutoDiscoveryCITypeRelation.get_by(peer_type_id=type_id, to_dict=False):
item.delete(commit=False)
for item in CITypeInheritance.get_by(parent_id=type_id, to_dict=False):
item.delete(commit=False)
@@ -271,9 +263,6 @@ class CITypeManager(object):
except Exception:
pass
for item in TopologyView.get_by(central_node_type=type_id, to_dict=False):
item.delete(commit=False)
db.session.commit()
ci_type.soft_delete()
@@ -693,19 +682,9 @@ class CITypeAttributeManager(object):
item = CITypeTrigger.get_by(type_id=_type_id, attr_id=attr_id, to_dict=False, first=True)
item and item.soft_delete(commit=False)
for item in (CITypeRelation.get_by(parent_id=type_id, to_dict=False) +
CITypeRelation.get_by(child_id=type_id, to_dict=False)):
if item.parent_id == type_id and attr_id in (item.parent_attr_ids or []):
item_dict = item.to_dict()
pop_idx = item.parent_attr_ids.index(attr_id)
elif item.child_id == type_id and attr_id in (item.child_attr_ids or []):
item_dict = item.to_dict()
pop_idx = item.child_attr_ids.index(attr_id)
else:
continue
item.update(parent_attr_ids=item_dict['parent_attr_ids'].pop(pop_idx),
child_attr_ids=item_dict['child_attr_ids'].pop(pop_idx),
commit=False)
for item in (CITypeRelation.get_by(parent_id=type_id, parent_attr_id=attr_id, to_dict=False) +
CITypeRelation.get_by(child_id=type_id, child_attr_id=attr_id, to_dict=False)):
item.soft_delete(commit=False)
db.session.commit()
@@ -774,12 +753,10 @@ class CITypeRelationManager(object):
res[idx] = _item
res[idx]['parent'] = item.parent.to_dict()
if item.parent_id not in type2attributes:
type2attributes[item.parent_id] = [i[1].to_dict() for i in
CITypeAttributeManager.get_all_attributes(item.parent_id)]
type2attributes[item.parent_id] = [i[1].to_dict() for i in CITypeAttributesCache.get2(item.parent_id)]
res[idx]['child'] = item.child.to_dict()
if item.child_id not in type2attributes:
type2attributes[item.child_id] = [i[1].to_dict() for i in
CITypeAttributeManager.get_all_attributes(item.child_id)]
type2attributes[item.child_id] = [i[1].to_dict() for i in CITypeAttributesCache.get2(item.child_id)]
res[idx]['relation_type'] = item.relation_type.to_dict()
return res, type2attributes
@@ -814,8 +791,8 @@ class CITypeRelationManager(object):
ci_type_dict["relation_type"] = relation_inst.relation_type.name
ci_type_dict["constraint"] = relation_inst.constraint
ci_type_dict["parent_attr_ids"] = relation_inst.parent_attr_ids
ci_type_dict["child_attr_ids"] = relation_inst.child_attr_ids
ci_type_dict["parent_attr_id"] = relation_inst.parent_attr_id
ci_type_dict["child_attr_id"] = relation_inst.child_attr_id
return ci_type_dict
@@ -848,70 +825,6 @@ class CITypeRelationManager(object):
return [cls._wrap_relation_type_dict(parent.parent_id, parent) for parent in parents]
@staticmethod
def get_relations_by_type_id(type_id):
nodes, edges = [], []
node_ids, edge_tuples = set(), set()
ci_type = CITypeCache.get(type_id)
if ci_type is None:
return nodes, edges
nodes.append(ci_type.to_dict())
node_ids.add(ci_type.id)
def _find(_id, lv):
lv += 1
for i in CITypeRelation.get_by(parent_id=_id, to_dict=False):
if i.child_id not in node_ids:
node_ids.add(i.child_id)
node = i.child.to_dict()
node.setdefault('level', []).append(lv)
nodes.append(node)
edges.append(dict(from_id=i.parent_id, to_id=i.child_id, text=i.relation_type.name, reverse=False))
edge_tuples.add((i.parent_id, i.child_id))
_find(i.child_id, lv)
continue
elif (i.parent_id, i.child_id) not in edge_tuples:
edges.append(dict(from_id=i.parent_id, to_id=i.child_id, text=i.relation_type.name, reverse=False))
edge_tuples.add((i.parent_id, i.child_id))
_find(i.child_id, lv)
for _node in nodes:
if _node['id'] == i.child_id and lv not in _node['level']:
_node['level'].append(lv)
def _reverse_find(_id, lv):
lv -= 1
for i in CITypeRelation.get_by(child_id=_id, to_dict=False):
if i.parent_id not in node_ids:
node_ids.add(i.parent_id)
node = i.parent.to_dict()
node.setdefault('level', []).append(lv)
nodes.append(node)
edges.append(dict(from_id=i.parent_id, to_id=i.child_id, text=i.relation_type.name, reverse=True))
edge_tuples.add((i.parent_id, i.child_id))
_reverse_find(i.parent_id, lv)
continue
elif (i.parent_id, i.child_id) not in edge_tuples:
edges.append(dict(from_id=i.parent_id, to_id=i.child_id, text=i.relation_type.name, reverse=True))
edge_tuples.add((i.parent_id, i.child_id))
_reverse_find(i.parent_id, lv)
for _node in nodes:
if _node['id'] == i.child_id and lv not in _node['level']:
_node['level'].append(lv)
level = 0
_reverse_find(ci_type.id, level)
level = 0
_find(ci_type.id, level)
return nodes, edges
@staticmethod
def _get(parent_id, child_id):
return CITypeRelation.get_by(parent_id=parent_id,
@@ -925,7 +838,7 @@ class CITypeRelationManager(object):
@classmethod
def add(cls, parent, child, relation_type_id, constraint=ConstraintEnum.One2Many,
parent_attr_ids=None, child_attr_ids=None):
parent_attr_id=None, child_attr_id=None):
p = CITypeManager.check_is_existed(parent)
c = CITypeManager.check_is_existed(child)
@@ -940,22 +853,21 @@ class CITypeRelationManager(object):
current_app.logger.warning(str(e))
return abort(400, ErrFormat.circular_dependency_error)
old_parent_attr_ids, old_child_attr_ids = None, None
old_parent_attr_id = None
existed = cls._get(p.id, c.id)
if existed is not None:
old_parent_attr_ids = copy.deepcopy(existed.parent_attr_ids)
old_child_attr_ids = copy.deepcopy(existed.child_attr_ids)
old_parent_attr_id = existed.parent_attr_id
existed = existed.update(relation_type_id=relation_type_id,
constraint=constraint,
parent_attr_ids=parent_attr_ids,
child_attr_ids=child_attr_ids,
parent_attr_id=parent_attr_id,
child_attr_id=child_attr_id,
filter_none=False)
else:
existed = CITypeRelation.create(parent_id=p.id,
child_id=c.id,
relation_type_id=relation_type_id,
parent_attr_ids=parent_attr_ids,
child_attr_ids=child_attr_ids,
parent_attr_id=parent_attr_id,
child_attr_id=child_attr_id,
constraint=constraint)
if current_app.config.get("USE_ACL"):
@@ -969,10 +881,10 @@ class CITypeRelationManager(object):
current_user.username,
ResourceTypeEnum.CI_TYPE_RELATION)
if ((parent_attr_ids and parent_attr_ids != old_parent_attr_ids) or
(child_attr_ids and child_attr_ids != old_child_attr_ids)):
from api.tasks.cmdb import rebuild_relation_for_attribute_changed
rebuild_relation_for_attribute_changed.apply_async(args=(existed.to_dict(),))
if parent_attr_id and parent_attr_id != old_parent_attr_id:
if parent_attr_id and parent_attr_id != existed.parent_attr_id:
from api.tasks.cmdb import rebuild_relation_for_attribute_changed
rebuild_relation_for_attribute_changed.apply_async(args=(existed.to_dict()))
CITypeHistoryManager.add(CITypeOperateType.ADD_RELATION, p.id,
change=dict(parent=p.to_dict(), child=c.to_dict(), relation_type_id=relation_type_id))
@@ -1266,8 +1178,8 @@ class CITypeTemplateManager(object):
id2obj_dicts[added_id].get('child_id'),
id2obj_dicts[added_id].get('relation_type_id'),
id2obj_dicts[added_id].get('constraint'),
id2obj_dicts[added_id].get('parent_attr_ids'),
id2obj_dicts[added_id].get('child_attr_ids'),
id2obj_dicts[added_id].get('parent_attr_id'),
id2obj_dicts[added_id].get('child_attr_id'),
)
else:
obj = cls.create(flush=True, **id2obj_dicts[added_id])

View File

@@ -73,7 +73,6 @@ class ResourceTypeEnum(BaseEnum):
RELATION_VIEW = "RelationView" # read/update/delete/grant
CI_FILTER = "CIFilter" # read
PAGE = "page" # read
TOPOLOGY_VIEW = "TopologyView" # read/update/delete/grant
class PermEnum(BaseEnum):

View File

@@ -25,15 +25,14 @@ from api.lib.cmdb.resp_format import ErrFormat
from api.lib.exception import AbortException
from api.lib.perm.acl.acl import ACLManager
from api.models.cmdb import CITypeAttribute
from api.models.cmdb import CITypeGroup
from api.models.cmdb import CITypeGroupItem
from api.models.cmdb import CITypeRelation
from api.models.cmdb import PreferenceCITypeOrder
from api.models.cmdb import PreferenceRelationView
from api.models.cmdb import PreferenceSearchOption
from api.models.cmdb import PreferenceShowAttributes
from api.models.cmdb import PreferenceTreeView
from api.models.cmdb import CITypeGroup
from api.models.cmdb import CITypeGroupItem
class PreferenceManager(object):
pref_attr_cls = PreferenceShowAttributes
@@ -86,6 +85,7 @@ class PreferenceManager(object):
return dict(group_types=group_types, tree_types=tree_types, type_ids=list(type_ids))
@staticmethod
def get_types2(instance=False, tree=False):
"""

View File

@@ -144,8 +144,3 @@ class ErrFormat(CommonErrFormat):
cron_time_format_invalid = _l("Scheduling time format error") # 调度时间格式错误
reconciliation_title = _l("CMDB data reconciliation results") # CMDB数据合规检查结果
reconciliation_body = _l("Number of {} illegal: {}") # "{} 不合规数: {}"
topology_exists = _l("Topology view {} already exists") # 拓扑视图 {} 已经存在
topology_group_exists = _l("Topology group {} already exists") # 拓扑视图分组 {} 已经存在
# 因为该分组下定义了拓扑视图,不能删除
topo_view_exists_cannot_delete_group = _l("The group cannot be deleted because the topology view already exists")

View File

@@ -17,12 +17,10 @@ def search(query=None,
count=1,
sort=None,
excludes=None,
use_id_filter=False,
use_ci_filter=True):
use_id_filter=False):
if current_app.config.get("USE_ES"):
s = SearchFromES(query, fl, facet, page, ret_key, count, sort)
else:
s = SearchFromDB(query, fl, facet, page, ret_key, count, sort, excludes=excludes,
use_id_filter=use_id_filter, use_ci_filter=use_ci_filter)
s = SearchFromDB(query, fl, facet, page, ret_key, count, sort, excludes=excludes, use_id_filter=use_id_filter)
return s

View File

@@ -70,7 +70,6 @@ class Search(object):
self.valid_type_names = []
self.type2filter_perms = dict()
self.is_app_admin = is_app_admin('cmdb') or current_user.username == "worker"
self.is_app_admin = self.is_app_admin or (not self.use_ci_filter and not self.use_id_filter)
@staticmethod
def _operator_proc(key):

View File

@@ -1,251 +0,0 @@
# -*- coding:utf-8 -*-
import json
from flask import abort
from flask import current_app
from flask_login import current_user
from werkzeug.exceptions import BadRequest
from api.extensions import rd
from api.lib.cmdb.cache import AttributeCache
from api.lib.cmdb.cache import CITypeCache
from api.lib.cmdb.ci import CIRelationManager
from api.lib.cmdb.ci_type import CITypeRelationManager
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
from api.lib.cmdb.const import ResourceTypeEnum
from api.lib.cmdb.resp_format import ErrFormat
from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci import search
from api.lib.perm.acl.acl import ACLManager
from api.lib.perm.acl.acl import is_app_admin
from api.models.cmdb import TopologyView
from api.models.cmdb import TopologyViewGroup
class TopologyViewManager(object):
group_cls = TopologyViewGroup
cls = TopologyView
@classmethod
def get_name_by_id(cls, _id):
res = cls.cls.get_by_id(_id)
return res and res.name
def get_view_by_id(self, _id):
res = self.cls.get_by_id(_id)
return res and res.to_dict() or {}
@classmethod
def add_group(cls, name, order):
if order is None:
cur_max_order = cls.group_cls.get_by(only_query=True).order_by(cls.group_cls.order.desc()).first()
cur_max_order = cur_max_order and cur_max_order.order or 0
order = cur_max_order + 1
cls.group_cls.get_by(name=name, first=True, to_dict=False) and abort(
400, ErrFormat.topology_group_exists.format(name))
return cls.group_cls.create(name=name, order=order)
def update_group(self, group_id, name, view_ids):
existed = self.group_cls.get_by_id(group_id) or abort(404, ErrFormat.not_found)
if name is not None and name != existed.name:
existed.update(name=name)
for idx, view_id in enumerate(view_ids):
view = self.cls.get_by_id(view_id)
if view is not None:
view.update(group_id=group_id, order=idx)
return existed.to_dict()
@classmethod
def delete_group(cls, _id):
existed = cls.group_cls.get_by_id(_id) or abort(404, ErrFormat.not_found)
if cls.cls.get_by(group_id=_id, first=True):
return abort(400, ErrFormat.topo_view_exists_cannot_delete_group)
existed.soft_delete()
@classmethod
def group_order(cls, group_ids):
for idx, group_id in enumerate(group_ids):
group = cls.group_cls.get_by_id(group_id)
group.update(order=idx + 1)
@classmethod
def add(cls, name, group_id, option, order=None, **kwargs):
cls.cls.get_by(name=name, first=True) and abort(400, ErrFormat.topology_exists.format(name))
if order is None:
cur_max_order = cls.cls.get_by(group_id=group_id, only_query=True).order_by(
cls.cls.order.desc()).first()
cur_max_order = cur_max_order and cur_max_order.order or 0
order = cur_max_order + 1
inst = cls.cls.create(name=name, group_id=group_id, option=option, order=order, **kwargs).to_dict()
if current_app.config.get('USE_ACL'):
try:
ACLManager().add_resource(name, ResourceTypeEnum.TOPOLOGY_VIEW)
except BadRequest:
pass
ACLManager().grant_resource_to_role(name,
current_user.username,
ResourceTypeEnum.TOPOLOGY_VIEW)
return inst
@classmethod
def update(cls, _id, **kwargs):
existed = cls.cls.get_by_id(_id) or abort(404, ErrFormat.not_found)
existed_name = existed.name
inst = existed.update(filter_none=False, **kwargs).to_dict()
if current_app.config.get('USE_ACL') and existed_name != kwargs.get('name') and kwargs.get('name'):
try:
ACLManager().update_resource(existed_name, kwargs['name'], ResourceTypeEnum.TOPOLOGY_VIEW)
except BadRequest:
pass
return inst
@classmethod
def delete(cls, _id):
existed = cls.cls.get_by_id(_id) or abort(404, ErrFormat.not_found)
existed.soft_delete()
if current_app.config.get("USE_ACL"):
ACLManager().del_resource(existed.name, ResourceTypeEnum.TOPOLOGY_VIEW)
@classmethod
def group_inner_order(cls, _ids):
for idx, _id in enumerate(_ids):
topology = cls.cls.get_by_id(_id)
topology.update(order=idx + 1)
@classmethod
def get_all(cls):
resources = None
if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'):
resources = set([i.get('name') for i in ACLManager().get_resources(ResourceTypeEnum.TOPOLOGY_VIEW)])
groups = cls.group_cls.get_by(to_dict=True)
groups = sorted(groups, key=lambda x: x['order'])
group2pos = {group['id']: idx for idx, group in enumerate(groups)}
topo_views = sorted(cls.cls.get_by(to_dict=True), key=lambda x: x['order'])
other_group = dict(views=[])
for view in topo_views:
if resources is not None and view['name'] not in resources:
continue
if view['group_id']:
groups[group2pos[view['group_id']]].setdefault('views', []).append(view)
else:
other_group['views'].append(view)
if other_group['views']:
groups.append(other_group)
return groups
@staticmethod
def relation_from_ci_type(type_id):
nodes, edges = CITypeRelationManager.get_relations_by_type_id(type_id)
return dict(nodes=nodes, edges=edges)
def topology_view(self, view_id=None, preview=None):
if view_id is not None:
view = self.cls.get_by_id(view_id) or abort(404, ErrFormat.not_found)
central_node_type, central_node_instances, path = (view.central_node_type,
view.central_node_instances, view.path)
else:
central_node_type = preview.get('central_node_type')
central_node_instances = preview.get('central_node_instances')
path = preview.get('path')
nodes, links = [], []
_type = CITypeCache.get(central_node_type)
if not _type:
return dict(nodes=nodes, links=links)
root_ids = []
show_key = AttributeCache.get(_type.show_id or _type.unique_id)
q = (central_node_instances[2:] if central_node_instances.startswith('q=') else
central_node_instances)
s = search(q, fl=['_id', show_key.name], use_id_filter=False, use_ci_filter=False, count=1000000)
try:
response, _, _, _, _, _ = s.search()
except SearchError as e:
current_app.logger.info(e)
return dict(nodes=nodes, links=links)
for i in response:
root_ids.append(i['_id'])
nodes.append(dict(id=str(i['_id']), name=i[show_key.name], type_id=central_node_type))
if not root_ids:
return dict(nodes=nodes, links=links)
prefix = REDIS_PREFIX_CI_RELATION
key = list(map(str, root_ids))
id2node = {}
type2meta = {}
for level in sorted([i for i in path.keys() if int(i) > 0]):
type_ids = {int(i) for i in path[level]}
res = [json.loads(x).items() for x in [i or '{}' for i in rd.get(key, prefix) or []]]
new_key = []
for idx, from_id in enumerate(key):
for to_id, type_id in res[idx]:
if type_id in type_ids:
links.append({'from': from_id, 'to': to_id})
id2node[to_id] = {'id': to_id, 'type_id': type_id}
new_key.append(to_id)
if type_id not in type2meta:
type2meta[type_id] = CITypeCache.get(type_id).icon
key = new_key
ci_ids = list(map(int, root_ids))
for level in sorted([i for i in path.keys() if int(i) < 0]):
type_ids = {int(i) for i in path[level]}
res = CIRelationManager.get_parent_ids(ci_ids)
_ci_ids = []
for to_id in res:
for from_id, type_id in res[to_id]:
if type_id in type_ids:
from_id, to_id = str(from_id), str(to_id)
links.append({'from': from_id, 'to': to_id})
id2node[from_id] = {'id': str(from_id), 'type_id': type_id}
_ci_ids.append(from_id)
if type_id not in type2meta:
type2meta[type_id] = CITypeCache.get(type_id).icon
ci_ids = _ci_ids
fl = set()
type_ids = {t for lv in path if lv != '0' for t in path[lv]}
type2show = {}
for type_id in type_ids:
ci_type = CITypeCache.get(type_id)
if ci_type:
attr = AttributeCache.get(ci_type.show_id or ci_type.unique_id)
if attr:
fl.add(attr.name)
type2show[type_id] = attr.name
if id2node:
s = search("_id:({})".format(';'.join(id2node.keys())), fl=list(fl),
use_id_filter=False, use_ci_filter=False, count=1000000)
try:
response, _, _, _, _, _ = s.search()
except SearchError:
return dict(nodes=nodes, links=links)
for i in response:
id2node[str(i['_id'])]['name'] = i[type2show[str(i['_type'])]]
nodes.extend(id2node.values())
return dict(nodes=nodes, links=links, type2meta=type2meta)

View File

@@ -3,7 +3,6 @@ import functools
from flask import abort, session
from api.lib.common_setting.acl import ACLManager
from api.lib.common_setting.resp_format import ErrFormat
from api.lib.perm.acl.acl import is_app_admin
def perms_role_required(app_name, resource_type_name, resource_name, perm, role_name=None):
@@ -17,7 +16,7 @@ def perms_role_required(app_name, resource_type_name, resource_name, perm, role_
except Exception as e:
# resource_type not exist, continue check role
if role_name:
if role_name not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin(app_name):
if role_name not in session.get("acl", {}).get("parentRoles", []):
abort(403, ErrFormat.role_required.format(role_name))
return func(*args, **kwargs)
@@ -26,7 +25,7 @@ def perms_role_required(app_name, resource_type_name, resource_name, perm, role_
if not has_perms:
if role_name:
if role_name not in session.get("acl", {}).get("parentRoles", []) and not is_app_admin(app_name):
if role_name not in session.get("acl", {}).get("parentRoles", []):
abort(403, ErrFormat.role_required.format(role_name))
else:
abort(403, ErrFormat.resource_no_permission.format(resource_name, perm))

View File

@@ -48,12 +48,7 @@ class CMDBApp(BaseApp):
{"page": "Model_Relationships", "page_cn": "模型关系", "perms": ["read"]},
{"page": "Operation_Audit", "page_cn": "操作审计", "perms": ["read"]},
{"page": "Relationship_Types", "page_cn": "关系类型", "perms": ["read"]},
{"page": "Auto_Discovery", "page_cn": "自动发现", "perms": ["read"]},
{"page": "TopologyView", "page_cn": "拓扑视图",
"perms": ["read", "create_topology_group", "update_topology_group", "delete_topology_group",
"create_topology_view"],
},
]
{"page": "Auto_Discovery", "page_cn": "自动发现", "perms": ["read"]}]
def __init__(self):
super().__init__()

View File

@@ -3,9 +3,9 @@
import msgpack
import redis_lock
from flask import current_app
from api.extensions import cache
from api.extensions import db
from api.extensions import rd
from api.lib.decorator import flush_db
from api.models.acl import App
@@ -161,7 +161,6 @@ class RoleRelationCache(object):
def get_parent_ids(cls, rid, app_id, force=False):
parent_ids = cache.get(cls.PREFIX_PARENT.format(rid, app_id))
if not parent_ids or force:
db.session.commit()
from api.lib.perm.acl.role import RoleRelationCRUD
parent_ids = RoleRelationCRUD.get_parent_ids(rid, app_id)
cache.set(cls.PREFIX_PARENT.format(rid, app_id), parent_ids, timeout=0)
@@ -172,7 +171,6 @@ class RoleRelationCache(object):
def get_child_ids(cls, rid, app_id, force=False):
child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid, app_id))
if not child_ids or force:
db.session.commit()
from api.lib.perm.acl.role import RoleRelationCRUD
child_ids = RoleRelationCRUD.get_child_ids(rid, app_id)
cache.set(cls.PREFIX_CHILDREN.format(rid, app_id), child_ids, timeout=0)
@@ -189,7 +187,6 @@ class RoleRelationCache(object):
"""
resources = cache.get(cls.PREFIX_RESOURCES.format(rid, app_id))
if not resources or force:
db.session.commit()
from api.lib.perm.acl.role import RoleCRUD
resources = RoleCRUD.get_resources(rid, app_id)
if resources['id2perms'] or resources['group2perms']:
@@ -201,7 +198,6 @@ class RoleRelationCache(object):
def get_resources2(cls, rid, app_id, force=False):
r_g = cache.get(cls.PREFIX_RESOURCES2.format(rid, app_id))
if not r_g or force:
db.session.commit()
res = cls.get_resources(rid, app_id)
id2perms = res['id2perms']
group2perms = res['group2perms']

View File

@@ -315,12 +315,9 @@ class ResourceCRUD(object):
return resource
@staticmethod
def delete(_id, rebuild=True, app_id=None):
def delete(_id, rebuild=True):
resource = Resource.get_by_id(_id) or abort(404, ErrFormat.resource_not_found.format("id={}".format(_id)))
if app_id is not None and resource.app_id != app_id:
return abort(404, ErrFormat.resource_not_found.format("id={}".format(_id)))
origin = resource.to_dict()
resource.soft_delete()

View File

@@ -154,19 +154,19 @@ class RoleRelationCRUD(object):
if existed:
continue
if parent_id in cls.recursive_child_ids(child_id, app_id):
return abort(400, ErrFormat.inheritance_dead_loop)
result.append(RoleRelation.create(parent_id=parent_id, child_id=child_id, app_id=app_id).to_dict())
RoleRelationCache.clean(parent_id, app_id)
RoleRelationCache.clean(child_id, app_id)
if parent_id in cls.recursive_child_ids(child_id, app_id):
return abort(400, ErrFormat.inheritance_dead_loop)
if app_id is None:
for app in AppCRUD.get_all():
if app.name != "acl":
RoleRelationCache.clean(child_id, app.id)
result.append(RoleRelation.create(parent_id=parent_id, child_id=child_id, app_id=app_id).to_dict())
AuditCRUD.add_role_log(app_id, AuditOperateType.role_relation_add,
AuditScope.role_relation, role.id, {}, {},
{'child_ids': list(child_ids), 'parent_ids': [parent_id], }

View File

@@ -3,8 +3,8 @@ import os
import secrets
import sys
import threading
from base64 import b64decode, b64encode
from base64 import b64decode, b64encode
from Cryptodome.Protocol.SecretSharing import Shamir
from colorama import Back, Fore, Style, init as colorama_init
from cryptography.hazmat.backends import default_backend
@@ -30,7 +30,6 @@ seal_status = True
secrets_encrypt_key = ""
secrets_root_key = ""
def string_to_bytes(value):
if not value:
return ""
@@ -79,7 +78,7 @@ class KeyManage:
(len(sys.argv) > 1 and sys.argv[1] in ("run", "cmdb-password-data-migrate"))):
self.backend = backend
threading.Thread(target=self.watch_root_key, args=(app,), daemon=True).start()
threading.Thread(target=self.watch_root_key, args=(app,)).start()
self.trigger = app.config.get("INNER_TRIGGER_TOKEN")
if not self.trigger:
@@ -413,7 +412,7 @@ class KeyManage:
class InnerCrypt:
def __init__(self):
self.encrypt_key = b64decode(secrets_encrypt_key)
# self.encrypt_key = b64decode(secrets_encrypt_key, "".encode("utf-8"))
#self.encrypt_key = b64decode(secrets_encrypt_key, "".encode("utf-8"))
def encrypt(self, plaintext):
"""
@@ -491,4 +490,4 @@ if __name__ == "__main__":
t_ciphertext, status1 = c.encrypt(t_plaintext)
print("Ciphertext:", t_ciphertext)
decrypted_plaintext, status2 = c.decrypt(t_ciphertext)
print("Decrypted plaintext:", decrypted_plaintext)
print("Decrypted plaintext:", decrypted_plaintext)

View File

@@ -88,11 +88,11 @@ def webhook_request(webhook, payload):
params = webhook.get('parameters') or None
if isinstance(params, dict):
params = json.loads(Template(json.dumps(params)).render(payload).encode('utf-8'))
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).encode('utf-8')
data = Template(json.dumps(webhook.get('body', ''))).render(payload)
auth = _wrap_auth(**webhook.get('authorization', {}))
if (webhook.get('authorization', {}).get("type") or '').lower() == 'oauth2.0':

View File

@@ -79,11 +79,8 @@ class CITypeRelation(Model):
relation_type_id = db.Column(db.Integer, db.ForeignKey("c_relation_types.id"), nullable=False)
constraint = db.Column(db.Enum(*ConstraintEnum.all()), default=ConstraintEnum.One2Many)
parent_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id")) # CMDB > 2.4.5: deprecated
child_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id")) # CMDB > 2.4.5: deprecated
parent_attr_ids = db.Column(db.JSON) # [parent_attr_id, ]
child_attr_ids = db.Column(db.JSON) # [child_attr_id, ]
parent_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
child_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
parent = db.relationship("CIType", primaryjoin="CIType.id==CITypeRelation.parent_id")
child = db.relationship("CIType", primaryjoin="CIType.id==CITypeRelation.child_id")
@@ -212,26 +209,6 @@ class CITriggerHistory(Model):
webhook = db.Column(db.Text)
class TopologyViewGroup(Model):
__tablename__ = 'c_topology_view_groups'
name = db.Column(db.String(64), index=True)
order = db.Column(db.Integer, default=0)
class TopologyView(Model):
__tablename__ = 'c_topology_views'
name = db.Column(db.String(64), index=True)
group_id = db.Column(db.Integer, db.ForeignKey('c_topology_view_groups.id'))
category = db.Column(db.String(32))
central_node_type = db.Column(db.Integer)
central_node_instances = db.Column(db.Text)
path = db.Column(db.JSON)
order = db.Column(db.Integer, default=0)
option = db.Column(db.JSON)
class CITypeUniqueConstraint(Model):
__tablename__ = "c_c_t_u_c"

View File

@@ -113,17 +113,18 @@ def ci_delete_trigger(trigger, operate_type, ci_dict):
@reconnect_db
def ci_relation_cache(parent_id, child_id, ancestor_ids):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
if ancestor_ids is None:
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids,
first=True, to_dict=False)
if str(child_id) not in children:
children[str(child_id)] = cr.second_ci.type_id
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids,
first=True, to_dict=False)
if str(child_id) not in children:
children[str(child_id)] = cr.second_ci.type_id
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
if ancestor_ids is not None:
else:
key = "{},{}".format(ancestor_ids, parent_id)
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
grandson = json.loads(grandson) if grandson is not None else {}
@@ -190,15 +191,16 @@ def ci_relation_add(parent_dict, child_id, uid):
@reconnect_db
def ci_relation_delete(parent_id, child_id, ancestor_ids):
with redis_lock.Lock(rd.r, "CIRelation_{}".format(parent_id)):
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
if ancestor_ids is None:
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
children = json.loads(children) if children is not None else {}
if str(child_id) in children:
children.pop(str(child_id))
if str(child_id) in children:
children.pop(str(child_id))
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
if ancestor_ids is not None:
else:
key = "{},{}".format(ancestor_ids, parent_id)
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
grandson = json.loads(grandson) if grandson is not None else {}

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-05-28 18:05+0800\n"
"POT-Creation-Date: 2024-03-29 10:42+0800\n"
"PO-Revision-Date: 2023-12-25 20:21+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: zh\n"
@@ -284,198 +284,166 @@ msgstr "重复的触发器"
msgid "Trigger {} does not exist"
msgstr "触发器 {} 不存在"
#: api/lib/cmdb/resp_format.py:81
msgid "Duplicated reconciliation rule"
msgstr ""
#: api/lib/cmdb/resp_format.py:82
msgid "Reconciliation rule {} does not exist"
msgstr "关系类型 {} 不存在"
#: api/lib/cmdb/resp_format.py:84
msgid "Operation record {} does not exist"
msgstr "操作记录 {} 不存在"
#: api/lib/cmdb/resp_format.py:85
#: api/lib/cmdb/resp_format.py:83
msgid "Unique identifier cannot be deleted"
msgstr "不能删除唯一标识"
#: api/lib/cmdb/resp_format.py:86
#: api/lib/cmdb/resp_format.py:84
msgid "Cannot delete default sorted attributes"
msgstr "不能删除默认排序的属性"
#: api/lib/cmdb/resp_format.py:88
#: api/lib/cmdb/resp_format.py:86
msgid "No node selected"
msgstr "没有选择节点"
#: api/lib/cmdb/resp_format.py:89
#: api/lib/cmdb/resp_format.py:87
msgid "This search option does not exist!"
msgstr "该搜索选项不存在!"
#: api/lib/cmdb/resp_format.py:90
#: api/lib/cmdb/resp_format.py:88
msgid "This search option has a duplicate name!"
msgstr "该搜索选项命名重复!"
#: api/lib/cmdb/resp_format.py:92
#: api/lib/cmdb/resp_format.py:90
msgid "Relationship type {} already exists"
msgstr "关系类型 {} 已经存在"
#: api/lib/cmdb/resp_format.py:93
#: api/lib/cmdb/resp_format.py:91
msgid "Relationship type {} does not exist"
msgstr "关系类型 {} 不存在"
#: api/lib/cmdb/resp_format.py:95
#: api/lib/cmdb/resp_format.py:93
msgid "Invalid attribute value: {}"
msgstr "无效的属性值: {}"
#: api/lib/cmdb/resp_format.py:96
#: api/lib/cmdb/resp_format.py:94
msgid "{} Invalid value: {}"
msgstr "{} 无效的值: {}"
#: api/lib/cmdb/resp_format.py:97
#: api/lib/cmdb/resp_format.py:95
msgid "{} is not in the predefined values"
msgstr "{} 不在预定义值里"
#: api/lib/cmdb/resp_format.py:99
#: api/lib/cmdb/resp_format.py:97
msgid "The value of attribute {} must be unique, {} already exists"
msgstr "属性 {} 的值必须是唯一的, 当前值 {} 已存在"
#: api/lib/cmdb/resp_format.py:100
#: api/lib/cmdb/resp_format.py:98
msgid "Attribute {} value must exist"
msgstr "属性 {} 值必须存在"
#: api/lib/cmdb/resp_format.py:101
#: api/lib/cmdb/resp_format.py:99
msgid "Out of range value, the maximum value is 2147483647"
msgstr "超过最大值限制, 最大值是2147483647"
#: api/lib/cmdb/resp_format.py:103
#: api/lib/cmdb/resp_format.py:101
msgid "Unknown error when adding or modifying attribute value: {}"
msgstr "新增或者修改属性值未知错误: {}"
#: api/lib/cmdb/resp_format.py:105
#: api/lib/cmdb/resp_format.py:103
msgid "Duplicate custom name"
msgstr "订制名重复"
#: api/lib/cmdb/resp_format.py:107
#: api/lib/cmdb/resp_format.py:105
msgid "Number of models exceeds limit: {}"
msgstr "模型数超过限制: {}"
#: api/lib/cmdb/resp_format.py:108
#: api/lib/cmdb/resp_format.py:106
msgid "The number of CIs exceeds the limit: {}"
msgstr "CI数超过限制: {}"
#: api/lib/cmdb/resp_format.py:110
#: api/lib/cmdb/resp_format.py:108
msgid "Auto-discovery rule: {} already exists!"
msgstr "自动发现规则: {} 已经存在!"
#: api/lib/cmdb/resp_format.py:111
#: api/lib/cmdb/resp_format.py:109
msgid "Auto-discovery rule: {} does not exist!"
msgstr "自动发现规则: {} 不存在!"
#: api/lib/cmdb/resp_format.py:113
#: api/lib/cmdb/resp_format.py:111
msgid "This auto-discovery rule is referenced by the model and cannot be deleted!"
msgstr "该自动发现规则被模型引用, 不能删除!"
#: api/lib/cmdb/resp_format.py:115
#: api/lib/cmdb/resp_format.py:113
msgid "The application of auto-discovery rules cannot be defined repeatedly!"
msgstr "自动发现规则的应用不能重复定义!"
#: api/lib/cmdb/resp_format.py:116
#: api/lib/cmdb/resp_format.py:114
msgid "The auto-discovery you want to modify: {} does not exist!"
msgstr "您要修改的自动发现: {} 不存在!"
#: api/lib/cmdb/resp_format.py:117
#: api/lib/cmdb/resp_format.py:115
msgid "Attribute does not include unique identifier: {}"
msgstr "属性字段没有包括唯一标识: {}"
#: api/lib/cmdb/resp_format.py:118
#: api/lib/cmdb/resp_format.py:116
msgid "The auto-discovery instance does not exist!"
msgstr "自动发现的实例不存在!"
#: api/lib/cmdb/resp_format.py:119
#: api/lib/cmdb/resp_format.py:117
msgid "The model is not associated with this auto-discovery!"
msgstr "模型并未关联该自动发现!"
#: api/lib/cmdb/resp_format.py:120
#: api/lib/cmdb/resp_format.py:118
msgid "Only the creator can modify the Secret!"
msgstr "只有创建人才能修改Secret!"
#: api/lib/cmdb/resp_format.py:122
#: api/lib/cmdb/resp_format.py:120
msgid "This rule already has auto-discovery instances and cannot be deleted!"
msgstr "该规则已经有自动发现的实例, 不能被删除!"
#: api/lib/cmdb/resp_format.py:124
#: api/lib/cmdb/resp_format.py:122
msgid "The default auto-discovery rule is already referenced by model {}!"
msgstr "该默认的自动发现规则 已经被模型 {} 引用!"
#: api/lib/cmdb/resp_format.py:126
#: api/lib/cmdb/resp_format.py:124
msgid "The unique_key method must return a non-empty string!"
msgstr "unique_key方法必须返回非空字符串!"
#: api/lib/cmdb/resp_format.py:127
#: api/lib/cmdb/resp_format.py:125
msgid "The attributes method must return a list"
msgstr "attributes方法必须返回的是list"
#: api/lib/cmdb/resp_format.py:129
#: api/lib/cmdb/resp_format.py:127
msgid "The list returned by the attributes method cannot be empty!"
msgstr "attributes方法返回的list不能为空!"
#: api/lib/cmdb/resp_format.py:131
#: api/lib/cmdb/resp_format.py:129
msgid "Only administrators can define execution targets as: all nodes!"
msgstr "只有管理员才可以定义执行机器为: 所有节点!"
#: api/lib/cmdb/resp_format.py:132
#: api/lib/cmdb/resp_format.py:130
msgid "Execute targets permission check failed: {}"
msgstr "执行机器权限检查不通过: {}"
#: api/lib/cmdb/resp_format.py:134
#: api/lib/cmdb/resp_format.py:132
msgid "CI filter authorization must be named!"
msgstr "CI过滤授权 必须命名!"
#: api/lib/cmdb/resp_format.py:135
#: api/lib/cmdb/resp_format.py:133
msgid "CI filter authorization is currently not supported or query"
msgstr "CI过滤授权 暂时不支持 或 查询"
#: api/lib/cmdb/resp_format.py:138
#: api/lib/cmdb/resp_format.py:136
msgid "You do not have permission to operate attribute {}!"
msgstr "您没有属性 {} 的操作权限!"
#: api/lib/cmdb/resp_format.py:139
#: api/lib/cmdb/resp_format.py:137
msgid "You do not have permission to operate this CI!"
msgstr "您没有该CI的操作权限!"
#: api/lib/cmdb/resp_format.py:141
#: api/lib/cmdb/resp_format.py:139
msgid "Failed to save password: {}"
msgstr "保存密码失败: {}"
#: api/lib/cmdb/resp_format.py:142
#: api/lib/cmdb/resp_format.py:140
msgid "Failed to get password: {}"
msgstr "获取密码失败: {}"
#: api/lib/cmdb/resp_format.py:144
msgid "Scheduling time format error"
msgstr "{}格式错误,应该为:%Y-%m-%d %H:%M:%S"
#: api/lib/cmdb/resp_format.py:145
msgid "CMDB data reconciliation results"
msgstr ""
#: api/lib/cmdb/resp_format.py:146
msgid "Number of {} illegal: {}"
msgstr ""
#: api/lib/cmdb/resp_format.py:148
msgid "Topology view {} already exists"
msgstr "拓扑视图 {} 已经存在"
#: api/lib/cmdb/resp_format.py:149
msgid "Topology group {} already exists"
msgstr "拓扑视图分组 {} 已经存在"
#: api/lib/cmdb/resp_format.py:151
msgid "The group cannot be deleted because the topology view already exists"
msgstr "因为该分组下定义了拓扑视图,不能删除"
#: api/lib/common_setting/resp_format.py:8
msgid "Company info already existed"
msgstr "公司信息已存在,无法创建!"
@@ -732,10 +700,6 @@ msgstr "LDAP测试用户名必填"
msgid "Company wide"
msgstr "全公司"
#: api/lib/common_setting/resp_format.py:84
msgid "No permission to access resource {}, perm {} "
msgstr "您没有资源: {} 的 {} 权限"
#: api/lib/perm/acl/resp_format.py:9
msgid "login successful"
msgstr "登录成功"

View File

@@ -57,10 +57,10 @@ class CITypeRelationView(APIView):
def post(self, parent_id, child_id):
relation_type_id = request.values.get("relation_type_id")
constraint = request.values.get("constraint")
parent_attr_ids = request.values.get("parent_attr_ids")
child_attr_ids = request.values.get("child_attr_ids")
parent_attr_id = request.values.get("parent_attr_id")
child_attr_id = request.values.get("child_attr_id")
ctr_id = CITypeRelationManager.add(parent_id, child_id, relation_type_id, constraint,
parent_attr_ids, child_attr_ids)
parent_attr_id, child_attr_id)
return self.jsonify(ctr_id=ctr_id)

View File

@@ -1,178 +0,0 @@
# -*- coding:utf-8 -*-
from flask import abort
from flask import request
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum
from api.lib.cmdb.resp_format import ErrFormat
from api.lib.cmdb.topology import TopologyViewManager
from api.lib.common_setting.decorator import perms_role_required
from api.lib.common_setting.role_perm_base import CMDBApp
from api.lib.decorator import args_required
from api.lib.decorator import args_validate
from api.lib.perm.acl.acl import ACLManager
from api.lib.perm.acl.acl import has_perm_from_args
from api.lib.perm.acl.acl import is_app_admin
from api.resource import APIView
app_cli = CMDBApp()
class TopologyGroupView(APIView):
url_prefix = ('/topology_views/groups', '/topology_views/groups/<int:group_id>')
@args_required('name')
@args_validate(TopologyViewManager.group_cls)
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.TopologyView,
app_cli.op.create_topology_group, app_cli.admin_name)
def post(self):
name = request.values.get('name')
order = request.values.get('order')
group = TopologyViewManager.add_group(name, order)
return self.jsonify(group.to_dict())
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.TopologyView,
app_cli.op.update_topology_group, app_cli.admin_name)
def put(self, group_id):
name = request.values.get('name')
view_ids = request.values.get('view_ids')
group = TopologyViewManager().update_group(group_id, name, view_ids)
return self.jsonify(**group)
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.TopologyView,
app_cli.op.delete_topology_group, app_cli.admin_name)
def delete(self, group_id):
TopologyViewManager.delete_group(group_id)
return self.jsonify(group_id=group_id)
class TopologyGroupOrderView(APIView):
url_prefix = ('/topology_views/groups/order',)
@args_required('group_ids')
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.TopologyView,
app_cli.op.update_topology_group, app_cli.admin_name)
def post(self):
group_ids = request.values.get('group_ids')
TopologyViewManager.group_order(group_ids)
return self.jsonify(group_ids=group_ids)
def put(self):
return self.post()
class TopologyView(APIView):
url_prefix = ('/topology_views', '/topology_views/relations/ci_types/<int:type_id>', '/topology_views/<int:_id>')
def get(self, type_id=None, _id=None):
if type_id is not None:
return self.jsonify(TopologyViewManager.relation_from_ci_type(type_id))
if _id is not None:
return self.jsonify(TopologyViewManager().get_view_by_id(_id))
return self.jsonify(TopologyViewManager.get_all())
@args_required('name', 'central_node_type', 'central_node_instances', 'path', 'group_id')
@args_validate(TopologyViewManager.cls)
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.TopologyView,
app_cli.op.create_topology_view, app_cli.admin_name)
def post(self):
name = request.values.pop('name')
group_id = request.values.pop('group_id', None)
option = request.values.pop('option', None)
order = request.values.pop('order', None)
topo_view = TopologyViewManager.add(name, group_id, option, order, **request.values)
return self.jsonify(topo_view)
@args_validate(TopologyViewManager.cls)
@has_perm_from_args("_id", ResourceTypeEnum.TOPOLOGY_VIEW, PermEnum.UPDATE, TopologyViewManager.get_name_by_id)
def put(self, _id):
topo_view = TopologyViewManager.update(_id, **request.values)
return self.jsonify(topo_view)
@has_perm_from_args("_id", ResourceTypeEnum.TOPOLOGY_VIEW, PermEnum.DELETE, TopologyViewManager.get_name_by_id)
def delete(self, _id):
TopologyViewManager.delete(_id)
return self.jsonify(code=200)
class TopologyOrderView(APIView):
url_prefix = ('/topology_views/order',)
@args_required('view_ids')
@perms_role_required(app_cli.app_name, app_cli.resource_type_name, app_cli.op.TopologyView,
app_cli.op.create_topology_view, app_cli.admin_name)
def post(self):
view_ids = request.values.get('view_ids')
TopologyViewManager.group_inner_order(view_ids)
return self.jsonify(view_ids=view_ids)
def put(self):
return self.post()
class TopologyViewPreview(APIView):
url_prefix = ('/topology_views/preview', '/topology_views/<int:_id>/view')
def get(self, _id=None):
if _id is not None:
acl = ACLManager('cmdb')
resource_name = TopologyViewManager.get_name_by_id(_id)
if (not acl.has_permission(resource_name, ResourceTypeEnum.TOPOLOGY_VIEW, PermEnum.READ) and
not is_app_admin('cmdb')):
return abort(403, ErrFormat.no_permission.format(resource_name, PermEnum.READ))
return self.jsonify(TopologyViewManager().topology_view(view_id=_id))
else:
return self.jsonify(TopologyViewManager().topology_view(preview=request.values))
def post(self, _id=None):
return self.get(_id)
class TopologyViewGrantView(APIView):
url_prefix = "/topology_views/<int:view_id>/roles/<int:rid>/grant"
def post(self, view_id, rid):
perms = request.values.pop('perms', None)
view_name = TopologyViewManager.get_name_by_id(view_id) or abort(404, ErrFormat.not_found)
acl = ACLManager('cmdb')
if not acl.has_permission(view_name, ResourceTypeEnum.TOPOLOGY_VIEW,
PermEnum.GRANT) and not is_app_admin('cmdb'):
return abort(403, ErrFormat.no_permission.format(view_name, PermEnum.GRANT))
acl.grant_resource_to_role_by_rid(view_name, rid, ResourceTypeEnum.TOPOLOGY_VIEW, perms, rebuild=True)
return self.jsonify(code=200)
class TopologyViewRevokeView(APIView):
url_prefix = "/topology_views/<int:view_id>/roles/<int:rid>/revoke"
@args_required('perms')
def post(self, view_id, rid):
perms = request.values.pop('perms', None)
view_name = TopologyViewManager.get_name_by_id(view_id) or abort(404, ErrFormat.not_found)
acl = ACLManager('cmdb')
if not acl.has_permission(view_name, ResourceTypeEnum.TOPOLOGY_VIEW,
PermEnum.GRANT) and not is_app_admin('cmdb'):
return abort(403, ErrFormat.no_permission.format(view_name, PermEnum.GRANT))
acl.revoke_resource_from_role_by_rid(view_name, rid, ResourceTypeEnum.TOPOLOGY_VIEW, perms, rebuild=True)
return self.jsonify(code=200)

View File

@@ -1,7 +1,7 @@
-i https://mirrors.aliyun.com/pypi/simple
alembic==1.7.7
bs4==0.0.1
celery==5.3.1
celery>=5.3.1
celery-once==3.0.1
click==8.1.3
elasticsearch==7.17.9
@@ -53,4 +53,4 @@ shamir~=17.12.0
pycryptodomex>=3.19.0
colorama>=0.4.6
lz4>=4.3.2
python-magic==0.4.27
python-magic==0.4.27

View File

@@ -20,16 +20,10 @@ DEBUG_TB_INTERCEPT_REDIRECTS = False
ERROR_CODES = [400, 401, 403, 404, 405, 500, 502]
MYSQL_USER = env.str('MYSQL_USER', default='cmdb')
MYSQL_PASSWORD = env.str('MYSQL_PASSWORD', default='123456')
MYSQL_HOST = env.str('MYSQL_HOST', default='127.0.0.1')
MYSQL_PORT = env.int('MYSQL_PORT', default=3306)
MYSQL_DATABASE = env.str('MYSQL_DATABASE', default='cmdb')
# # database
SQLALCHEMY_DATABASE_URI = f'mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@' \
f'{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}?charset=utf8'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
SQLALCHEMY_BINDS = {
'user': SQLALCHEMY_DATABASE_URI
'user': 'mysql+pymysql://{user}:{password}@127.0.0.1:3306/{db}?charset=utf8'
}
SQLALCHEMY_ECHO = False
SQLALCHEMY_TRACK_MODIFICATIONS = False

View File

@@ -39,7 +39,7 @@
"md5": "^2.2.1",
"moment": "^2.24.0",
"nprogress": "^0.2.0",
"relation-graph": "^2.1.42",
"relation-graph": "^1.1.0",
"snabbdom": "^3.5.1",
"sortablejs": "1.9.0",
"style-resources-loader": "^1.5.0",

View File

@@ -54,252 +54,6 @@
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe92b;</span>
<div class="name">ops-topology_view</div>
<div class="code-name">&amp;#xe92b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe92a;</span>
<div class="name">monitor-host_analysis</div>
<div class="code-name">&amp;#xe92a;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe929;</span>
<div class="name">monitor-add2</div>
<div class="code-name">&amp;#xe929;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe928;</span>
<div class="name">monitor-native</div>
<div class="code-name">&amp;#xe928;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe927;</span>
<div class="name">veops-filter2</div>
<div class="code-name">&amp;#xe927;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe601;</span>
<div class="name">ops-cmdb-data_companies-selected</div>
<div class="code-name">&amp;#xe601;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe926;</span>
<div class="name">ops-cmdb-data_companies</div>
<div class="code-name">&amp;#xe926;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe921;</span>
<div class="name">monitor-threshold_value</div>
<div class="code-name">&amp;#xe921;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe922;</span>
<div class="name">monitor-disposition</div>
<div class="code-name">&amp;#xe922;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe923;</span>
<div class="name">monitor-automatic_discovery</div>
<div class="code-name">&amp;#xe923;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe924;</span>
<div class="name">monitor-grouping_list</div>
<div class="code-name">&amp;#xe924;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe925;</span>
<div class="name">monitor-node_list</div>
<div class="code-name">&amp;#xe925;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe920;</span>
<div class="name">monitor-general_view</div>
<div class="code-name">&amp;#xe920;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe91b;</span>
<div class="name">monitor-network_topology</div>
<div class="code-name">&amp;#xe91b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe91c;</span>
<div class="name">monitor-node_management</div>
<div class="code-name">&amp;#xe91c;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe91d;</span>
<div class="name">monitor-alarm_policy</div>
<div class="code-name">&amp;#xe91d;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe91e;</span>
<div class="name">monitor-alarm</div>
<div class="code-name">&amp;#xe91e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe91f;</span>
<div class="name">monitor-healing</div>
<div class="code-name">&amp;#xe91f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe8d4;</span>
<div class="name">monitor-data_acquisition</div>
<div class="code-name">&amp;#xe8d4;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe91a;</span>
<div class="name">monitor-analysis</div>
<div class="code-name">&amp;#xe91a;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe89b;</span>
<div class="name">monitor-index</div>
<div class="code-name">&amp;#xe89b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe867;</span>
<div class="name">monitor-user_defined</div>
<div class="code-name">&amp;#xe867;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe861;</span>
<div class="name">monitor-database</div>
<div class="code-name">&amp;#xe861;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe865;</span>
<div class="name">monitor-common</div>
<div class="code-name">&amp;#xe865;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe866;</span>
<div class="name">veops-edit</div>
<div class="code-name">&amp;#xe866;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe863;</span>
<div class="name">veops-empower</div>
<div class="code-name">&amp;#xe863;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe864;</span>
<div class="name">veops-share</div>
<div class="code-name">&amp;#xe864;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe862;</span>
<div class="name">veops-export</div>
<div class="code-name">&amp;#xe862;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe860;</span>
<div class="name">veops-import</div>
<div class="code-name">&amp;#xe860;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe807;</span>
<div class="name">monitor-ip (1)</div>
<div class="code-name">&amp;#xe807;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe803;</span>
<div class="name">monitor-director</div>
<div class="code-name">&amp;#xe803;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe804;</span>
<div class="name">monitor-host</div>
<div class="code-name">&amp;#xe804;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe802;</span>
<div class="name">cmdb-log</div>
<div class="code-name">&amp;#xe802;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7ff;</span>
<div class="name">monitor-add</div>
<div class="code-name">&amp;#xe7ff;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7fc;</span>
<div class="name">monitor-down</div>
<div class="code-name">&amp;#xe7fc;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7fd;</span>
<div class="name">monitor-up</div>
<div class="code-name">&amp;#xe7fd;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7f9;</span>
<div class="name">itsm-unfold</div>
<div class="code-name">&amp;#xe7f9;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7f8;</span>
<div class="name">itsm-shrink</div>
<div class="code-name">&amp;#xe7f8;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7a1;</span>
<div class="name">monitor-data_comaparison2</div>
<div class="code-name">&amp;#xe7a1;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7f7;</span>
<div class="name">monitor-data_comaparison1</div>
<div class="code-name">&amp;#xe7f7;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe7a0;</span>
<div class="name">monitor-online</div>
<div class="code-name">&amp;#xe7a0;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe919;</span>
<div class="name">ops-setting-application-selected</div>
@@ -4866,9 +4620,9 @@
<pre><code class="language-css"
>@font-face {
font-family: 'iconfont';
src: url('iconfont.woff2?t=1716896994700') format('woff2'),
url('iconfont.woff?t=1716896994700') format('woff'),
url('iconfont.ttf?t=1716896994700') format('truetype');
src: url('iconfont.woff2?t=1713840593232') format('woff2'),
url('iconfont.woff?t=1713840593232') format('woff'),
url('iconfont.ttf?t=1713840593232') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -4894,375 +4648,6 @@
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont ops-topology_view"></span>
<div class="name">
ops-topology_view
</div>
<div class="code-name">.ops-topology_view
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-host_analysis"></span>
<div class="name">
monitor-host_analysis
</div>
<div class="code-name">.monitor-host_analysis
</div>
</li>
<li class="dib">
<span class="icon iconfont a-Group427319324"></span>
<div class="name">
monitor-add2
</div>
<div class="code-name">.a-Group427319324
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-native"></span>
<div class="name">
monitor-native
</div>
<div class="code-name">.monitor-native
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-filter2"></span>
<div class="name">
veops-filter2
</div>
<div class="code-name">.veops-filter2
</div>
</li>
<li class="dib">
<span class="icon iconfont ops-cmdb-data_companies-selected"></span>
<div class="name">
ops-cmdb-data_companies-selected
</div>
<div class="code-name">.ops-cmdb-data_companies-selected
</div>
</li>
<li class="dib">
<span class="icon iconfont ops-cmdb-data_companies"></span>
<div class="name">
ops-cmdb-data_companies
</div>
<div class="code-name">.ops-cmdb-data_companies
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-threshold_value"></span>
<div class="name">
monitor-threshold_value
</div>
<div class="code-name">.monitor-threshold_value
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-disposition"></span>
<div class="name">
monitor-disposition
</div>
<div class="code-name">.monitor-disposition
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-automatic_discovery"></span>
<div class="name">
monitor-automatic_discovery
</div>
<div class="code-name">.monitor-automatic_discovery
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-grouping_list"></span>
<div class="name">
monitor-grouping_list
</div>
<div class="code-name">.monitor-grouping_list
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-node_list"></span>
<div class="name">
monitor-node_list
</div>
<div class="code-name">.monitor-node_list
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-general_view"></span>
<div class="name">
monitor-general_view
</div>
<div class="code-name">.monitor-general_view
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-network_topology"></span>
<div class="name">
monitor-network_topology
</div>
<div class="code-name">.monitor-network_topology
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-node_management"></span>
<div class="name">
monitor-node_management
</div>
<div class="code-name">.monitor-node_management
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-alarm_policy"></span>
<div class="name">
monitor-alarm_policy
</div>
<div class="code-name">.monitor-alarm_policy
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-alarm"></span>
<div class="name">
monitor-alarm
</div>
<div class="code-name">.monitor-alarm
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-healing"></span>
<div class="name">
monitor-healing
</div>
<div class="code-name">.monitor-healing
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-data_acquisition"></span>
<div class="name">
monitor-data_acquisition
</div>
<div class="code-name">.monitor-data_acquisition
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-analysis"></span>
<div class="name">
monitor-analysis
</div>
<div class="code-name">.monitor-analysis
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-index"></span>
<div class="name">
monitor-index
</div>
<div class="code-name">.monitor-index
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-user_defined"></span>
<div class="name">
monitor-user_defined
</div>
<div class="code-name">.monitor-user_defined
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-database"></span>
<div class="name">
monitor-database
</div>
<div class="code-name">.monitor-database
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-common"></span>
<div class="name">
monitor-common
</div>
<div class="code-name">.monitor-common
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-edit"></span>
<div class="name">
veops-edit
</div>
<div class="code-name">.veops-edit
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-empower"></span>
<div class="name">
veops-empower
</div>
<div class="code-name">.veops-empower
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-share"></span>
<div class="name">
veops-share
</div>
<div class="code-name">.veops-share
</div>
</li>
<li class="dib">
<span class="icon iconfont veops-export"></span>
<div class="name">
veops-export
</div>
<div class="code-name">.veops-export
</div>
</li>
<li class="dib">
<span class="icon iconfont a-veops-import1"></span>
<div class="name">
veops-import
</div>
<div class="code-name">.a-veops-import1
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-ip"></span>
<div class="name">
monitor-ip (1)
</div>
<div class="code-name">.monitor-ip
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-director"></span>
<div class="name">
monitor-director
</div>
<div class="code-name">.monitor-director
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-host"></span>
<div class="name">
monitor-host
</div>
<div class="code-name">.monitor-host
</div>
</li>
<li class="dib">
<span class="icon iconfont a-cmdb-log1"></span>
<div class="name">
cmdb-log
</div>
<div class="code-name">.a-cmdb-log1
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-add"></span>
<div class="name">
monitor-add
</div>
<div class="code-name">.monitor-add
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-down"></span>
<div class="name">
monitor-down
</div>
<div class="code-name">.monitor-down
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-up"></span>
<div class="name">
monitor-up
</div>
<div class="code-name">.monitor-up
</div>
</li>
<li class="dib">
<span class="icon iconfont itsm-unfold"></span>
<div class="name">
itsm-unfold
</div>
<div class="code-name">.itsm-unfold
</div>
</li>
<li class="dib">
<span class="icon iconfont itsm-stretch"></span>
<div class="name">
itsm-shrink
</div>
<div class="code-name">.itsm-stretch
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-data_comaparison2"></span>
<div class="name">
monitor-data_comaparison2
</div>
<div class="code-name">.monitor-data_comaparison2
</div>
</li>
<li class="dib">
<span class="icon iconfont monitor-data_comaparison1"></span>
<div class="name">
monitor-data_comaparison1
</div>
<div class="code-name">.monitor-data_comaparison1
</div>
</li>
<li class="dib">
<span class="icon iconfont a-monitor-online1"></span>
<div class="name">
monitor-online
</div>
<div class="code-name">.a-monitor-online1
</div>
</li>
<li class="dib">
<span class="icon iconfont ops-setting-application-selected"></span>
<div class="name">
@@ -12112,334 +11497,6 @@
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-topology_view"></use>
</svg>
<div class="name">ops-topology_view</div>
<div class="code-name">#ops-topology_view</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-host_analysis"></use>
</svg>
<div class="name">monitor-host_analysis</div>
<div class="code-name">#monitor-host_analysis</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-Group427319324"></use>
</svg>
<div class="name">monitor-add2</div>
<div class="code-name">#a-Group427319324</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-native"></use>
</svg>
<div class="name">monitor-native</div>
<div class="code-name">#monitor-native</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-filter2"></use>
</svg>
<div class="name">veops-filter2</div>
<div class="code-name">#veops-filter2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-cmdb-data_companies-selected"></use>
</svg>
<div class="name">ops-cmdb-data_companies-selected</div>
<div class="code-name">#ops-cmdb-data_companies-selected</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-cmdb-data_companies"></use>
</svg>
<div class="name">ops-cmdb-data_companies</div>
<div class="code-name">#ops-cmdb-data_companies</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-threshold_value"></use>
</svg>
<div class="name">monitor-threshold_value</div>
<div class="code-name">#monitor-threshold_value</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-disposition"></use>
</svg>
<div class="name">monitor-disposition</div>
<div class="code-name">#monitor-disposition</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-automatic_discovery"></use>
</svg>
<div class="name">monitor-automatic_discovery</div>
<div class="code-name">#monitor-automatic_discovery</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-grouping_list"></use>
</svg>
<div class="name">monitor-grouping_list</div>
<div class="code-name">#monitor-grouping_list</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-node_list"></use>
</svg>
<div class="name">monitor-node_list</div>
<div class="code-name">#monitor-node_list</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-general_view"></use>
</svg>
<div class="name">monitor-general_view</div>
<div class="code-name">#monitor-general_view</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-network_topology"></use>
</svg>
<div class="name">monitor-network_topology</div>
<div class="code-name">#monitor-network_topology</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-node_management"></use>
</svg>
<div class="name">monitor-node_management</div>
<div class="code-name">#monitor-node_management</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-alarm_policy"></use>
</svg>
<div class="name">monitor-alarm_policy</div>
<div class="code-name">#monitor-alarm_policy</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-alarm"></use>
</svg>
<div class="name">monitor-alarm</div>
<div class="code-name">#monitor-alarm</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-healing"></use>
</svg>
<div class="name">monitor-healing</div>
<div class="code-name">#monitor-healing</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-data_acquisition"></use>
</svg>
<div class="name">monitor-data_acquisition</div>
<div class="code-name">#monitor-data_acquisition</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-analysis"></use>
</svg>
<div class="name">monitor-analysis</div>
<div class="code-name">#monitor-analysis</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-index"></use>
</svg>
<div class="name">monitor-index</div>
<div class="code-name">#monitor-index</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-user_defined"></use>
</svg>
<div class="name">monitor-user_defined</div>
<div class="code-name">#monitor-user_defined</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-database"></use>
</svg>
<div class="name">monitor-database</div>
<div class="code-name">#monitor-database</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-common"></use>
</svg>
<div class="name">monitor-common</div>
<div class="code-name">#monitor-common</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-edit"></use>
</svg>
<div class="name">veops-edit</div>
<div class="code-name">#veops-edit</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-empower"></use>
</svg>
<div class="name">veops-empower</div>
<div class="code-name">#veops-empower</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-share"></use>
</svg>
<div class="name">veops-share</div>
<div class="code-name">#veops-share</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#veops-export"></use>
</svg>
<div class="name">veops-export</div>
<div class="code-name">#veops-export</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-veops-import1"></use>
</svg>
<div class="name">veops-import</div>
<div class="code-name">#a-veops-import1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-ip"></use>
</svg>
<div class="name">monitor-ip (1)</div>
<div class="code-name">#monitor-ip</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-director"></use>
</svg>
<div class="name">monitor-director</div>
<div class="code-name">#monitor-director</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-host"></use>
</svg>
<div class="name">monitor-host</div>
<div class="code-name">#monitor-host</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-cmdb-log1"></use>
</svg>
<div class="name">cmdb-log</div>
<div class="code-name">#a-cmdb-log1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-add"></use>
</svg>
<div class="name">monitor-add</div>
<div class="code-name">#monitor-add</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-down"></use>
</svg>
<div class="name">monitor-down</div>
<div class="code-name">#monitor-down</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-up"></use>
</svg>
<div class="name">monitor-up</div>
<div class="code-name">#monitor-up</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#itsm-unfold"></use>
</svg>
<div class="name">itsm-unfold</div>
<div class="code-name">#itsm-unfold</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#itsm-stretch"></use>
</svg>
<div class="name">itsm-shrink</div>
<div class="code-name">#itsm-stretch</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-data_comaparison2"></use>
</svg>
<div class="name">monitor-data_comaparison2</div>
<div class="code-name">#monitor-data_comaparison2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#monitor-data_comaparison1"></use>
</svg>
<div class="name">monitor-data_comaparison1</div>
<div class="code-name">#monitor-data_comaparison1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#a-monitor-online1"></use>
</svg>
<div class="name">monitor-online</div>
<div class="code-name">#a-monitor-online1</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#ops-setting-application-selected"></use>

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 3857903 */
src: url('iconfont.woff2?t=1716896994700') format('woff2'),
url('iconfont.woff?t=1716896994700') format('woff'),
url('iconfont.ttf?t=1716896994700') format('truetype');
src: url('iconfont.woff2?t=1713840593232') format('woff2'),
url('iconfont.woff?t=1713840593232') format('woff'),
url('iconfont.ttf?t=1713840593232') format('truetype');
}
.iconfont {
@@ -13,170 +13,6 @@
-moz-osx-font-smoothing: grayscale;
}
.ops-topology_view:before {
content: "\e92b";
}
.monitor-host_analysis:before {
content: "\e92a";
}
.a-Group427319324:before {
content: "\e929";
}
.monitor-native:before {
content: "\e928";
}
.veops-filter2:before {
content: "\e927";
}
.ops-cmdb-data_companies-selected:before {
content: "\e601";
}
.ops-cmdb-data_companies:before {
content: "\e926";
}
.monitor-threshold_value:before {
content: "\e921";
}
.monitor-disposition:before {
content: "\e922";
}
.monitor-automatic_discovery:before {
content: "\e923";
}
.monitor-grouping_list:before {
content: "\e924";
}
.monitor-node_list:before {
content: "\e925";
}
.monitor-general_view:before {
content: "\e920";
}
.monitor-network_topology:before {
content: "\e91b";
}
.monitor-node_management:before {
content: "\e91c";
}
.monitor-alarm_policy:before {
content: "\e91d";
}
.monitor-alarm:before {
content: "\e91e";
}
.monitor-healing:before {
content: "\e91f";
}
.monitor-data_acquisition:before {
content: "\e8d4";
}
.monitor-analysis:before {
content: "\e91a";
}
.monitor-index:before {
content: "\e89b";
}
.monitor-user_defined:before {
content: "\e867";
}
.monitor-database:before {
content: "\e861";
}
.monitor-common:before {
content: "\e865";
}
.veops-edit:before {
content: "\e866";
}
.veops-empower:before {
content: "\e863";
}
.veops-share:before {
content: "\e864";
}
.veops-export:before {
content: "\e862";
}
.a-veops-import1:before {
content: "\e860";
}
.monitor-ip:before {
content: "\e807";
}
.monitor-director:before {
content: "\e803";
}
.monitor-host:before {
content: "\e804";
}
.a-cmdb-log1:before {
content: "\e802";
}
.monitor-add:before {
content: "\e7ff";
}
.monitor-down:before {
content: "\e7fc";
}
.monitor-up:before {
content: "\e7fd";
}
.itsm-unfold:before {
content: "\e7f9";
}
.itsm-stretch:before {
content: "\e7f8";
}
.monitor-data_comaparison2:before {
content: "\e7a1";
}
.monitor-data_comaparison1:before {
content: "\e7f7";
}
.a-monitor-online1:before {
content: "\e7a0";
}
.ops-setting-application-selected:before {
content: "\e919";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,293 +5,6 @@
"css_prefix_text": "",
"description": "",
"glyphs": [
{
"icon_id": "40499246",
"name": "ops-topology_view",
"font_class": "ops-topology_view",
"unicode": "e92b",
"unicode_decimal": 59691
},
{
"icon_id": "40411336",
"name": "monitor-host_analysis",
"font_class": "monitor-host_analysis",
"unicode": "e92a",
"unicode_decimal": 59690
},
{
"icon_id": "40372105",
"name": "monitor-add2",
"font_class": "a-Group427319324",
"unicode": "e929",
"unicode_decimal": 59689
},
{
"icon_id": "40368097",
"name": "monitor-native",
"font_class": "monitor-native",
"unicode": "e928",
"unicode_decimal": 59688
},
{
"icon_id": "40357355",
"name": "veops-filter2",
"font_class": "veops-filter2",
"unicode": "e927",
"unicode_decimal": 59687
},
{
"icon_id": "40356229",
"name": "ops-cmdb-data_companies-selected",
"font_class": "ops-cmdb-data_companies-selected",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "40343814",
"name": "ops-cmdb-data_companies",
"font_class": "ops-cmdb-data_companies",
"unicode": "e926",
"unicode_decimal": 59686
},
{
"icon_id": "40326916",
"name": "monitor-threshold_value",
"font_class": "monitor-threshold_value",
"unicode": "e921",
"unicode_decimal": 59681
},
{
"icon_id": "40326913",
"name": "monitor-disposition",
"font_class": "monitor-disposition",
"unicode": "e922",
"unicode_decimal": 59682
},
{
"icon_id": "40326911",
"name": "monitor-automatic_discovery",
"font_class": "monitor-automatic_discovery",
"unicode": "e923",
"unicode_decimal": 59683
},
{
"icon_id": "40326907",
"name": "monitor-grouping_list",
"font_class": "monitor-grouping_list",
"unicode": "e924",
"unicode_decimal": 59684
},
{
"icon_id": "40326906",
"name": "monitor-node_list",
"font_class": "monitor-node_list",
"unicode": "e925",
"unicode_decimal": 59685
},
{
"icon_id": "40326667",
"name": "monitor-general_view",
"font_class": "monitor-general_view",
"unicode": "e920",
"unicode_decimal": 59680
},
{
"icon_id": "40326683",
"name": "monitor-network_topology",
"font_class": "monitor-network_topology",
"unicode": "e91b",
"unicode_decimal": 59675
},
{
"icon_id": "40326682",
"name": "monitor-node_management",
"font_class": "monitor-node_management",
"unicode": "e91c",
"unicode_decimal": 59676
},
{
"icon_id": "40326681",
"name": "monitor-alarm_policy",
"font_class": "monitor-alarm_policy",
"unicode": "e91d",
"unicode_decimal": 59677
},
{
"icon_id": "40326680",
"name": "monitor-alarm",
"font_class": "monitor-alarm",
"unicode": "e91e",
"unicode_decimal": 59678
},
{
"icon_id": "40326679",
"name": "monitor-healing",
"font_class": "monitor-healing",
"unicode": "e91f",
"unicode_decimal": 59679
},
{
"icon_id": "40326685",
"name": "monitor-data_acquisition",
"font_class": "monitor-data_acquisition",
"unicode": "e8d4",
"unicode_decimal": 59604
},
{
"icon_id": "40326684",
"name": "monitor-analysis",
"font_class": "monitor-analysis",
"unicode": "e91a",
"unicode_decimal": 59674
},
{
"icon_id": "40308359",
"name": "monitor-index",
"font_class": "monitor-index",
"unicode": "e89b",
"unicode_decimal": 59547
},
{
"icon_id": "40307829",
"name": "monitor-user_defined",
"font_class": "monitor-user_defined",
"unicode": "e867",
"unicode_decimal": 59495
},
{
"icon_id": "40307835",
"name": "monitor-database",
"font_class": "monitor-database",
"unicode": "e861",
"unicode_decimal": 59489
},
{
"icon_id": "40307833",
"name": "monitor-common",
"font_class": "monitor-common",
"unicode": "e865",
"unicode_decimal": 59493
},
{
"icon_id": "40307035",
"name": "veops-edit",
"font_class": "veops-edit",
"unicode": "e866",
"unicode_decimal": 59494
},
{
"icon_id": "40307027",
"name": "veops-empower",
"font_class": "veops-empower",
"unicode": "e863",
"unicode_decimal": 59491
},
{
"icon_id": "40307026",
"name": "veops-share",
"font_class": "veops-share",
"unicode": "e864",
"unicode_decimal": 59492
},
{
"icon_id": "40306997",
"name": "veops-export",
"font_class": "veops-export",
"unicode": "e862",
"unicode_decimal": 59490
},
{
"icon_id": "40306881",
"name": "veops-import",
"font_class": "a-veops-import1",
"unicode": "e860",
"unicode_decimal": 59488
},
{
"icon_id": "40262335",
"name": "monitor-ip (1)",
"font_class": "monitor-ip",
"unicode": "e807",
"unicode_decimal": 59399
},
{
"icon_id": "40262337",
"name": "monitor-director",
"font_class": "monitor-director",
"unicode": "e803",
"unicode_decimal": 59395
},
{
"icon_id": "40262336",
"name": "monitor-host",
"font_class": "monitor-host",
"unicode": "e804",
"unicode_decimal": 59396
},
{
"icon_id": "40262216",
"name": "cmdb-log",
"font_class": "a-cmdb-log1",
"unicode": "e802",
"unicode_decimal": 59394
},
{
"icon_id": "40261712",
"name": "monitor-add",
"font_class": "monitor-add",
"unicode": "e7ff",
"unicode_decimal": 59391
},
{
"icon_id": "40248186",
"name": "monitor-down",
"font_class": "monitor-down",
"unicode": "e7fc",
"unicode_decimal": 59388
},
{
"icon_id": "40248184",
"name": "monitor-up",
"font_class": "monitor-up",
"unicode": "e7fd",
"unicode_decimal": 59389
},
{
"icon_id": "40235502",
"name": "itsm-unfold",
"font_class": "itsm-unfold",
"unicode": "e7f9",
"unicode_decimal": 59385
},
{
"icon_id": "40235453",
"name": "itsm-shrink",
"font_class": "itsm-stretch",
"unicode": "e7f8",
"unicode_decimal": 59384
},
{
"icon_id": "40217123",
"name": "monitor-data_comaparison2",
"font_class": "monitor-data_comaparison2",
"unicode": "e7a1",
"unicode_decimal": 59297
},
{
"icon_id": "40217122",
"name": "monitor-data_comaparison1",
"font_class": "monitor-data_comaparison1",
"unicode": "e7f7",
"unicode_decimal": 59383
},
{
"icon_id": "40176936",
"name": "monitor-online",
"font_class": "a-monitor-online1",
"unicode": "e7a0",
"unicode_decimal": 59296
},
{
"icon_id": "40043662",
"name": "ops-setting-application-selected",

Binary file not shown.

View File

@@ -9,7 +9,7 @@
:bodyStyle="{ padding: '24px 12px' }"
:placement="placement"
>
<ResourceSearch ref="resourceSearch" :fromCronJob="true" :type="type" :typeId="typeId" @copySuccess="copySuccess" />
<ResourceSearch :fromCronJob="true" @copySuccess="copySuccess" />
</CustomDrawer>
</template>
@@ -23,14 +23,6 @@ export default {
type: String,
default: 'right',
},
type: {
type: String,
default: 'resourceSearch'
},
typeId: {
type: Number,
default: null
}
},
data() {
return {

View File

@@ -182,8 +182,8 @@ export default {
<tag {...{ props, attrs }}>
{this.renderIcon({ icon: menu.meta.icon, customIcon: menu.meta.customIcon, name: menu.meta.name, typeId: menu.meta.typeId, routeName: menu.name, selectedIcon: menu.meta.selectedIcon, })}
<span>
<span style={menu.meta.style} class={this.renderI18n(menu.meta.title).length > 10 ? 'scroll' : ''}>{this.renderI18n(menu.meta.title)}</span>
{isShowDot && !menu.meta.disabled &&
<span class={this.renderI18n(menu.meta.title).length > 10 ? 'scroll' : ''}>{this.renderI18n(menu.meta.title)}</span>
{isShowDot &&
<a-popover
overlayClassName="custom-menu-extra-submenu"
placement="rightTop"

View File

@@ -81,7 +81,7 @@ export default {
if (route.name === 'cmdb') {
const preference = await getPreference()
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
if (lastTypeId && preference.type_ids.some((item) => item === Number(lastTypeId))) {
if (lastTypeId && preference.some((item) => item.id === Number(lastTypeId))) {
this.$router.push(`/cmdb/instances/types/${lastTypeId}`)
} else {
this.$router.push('/cmdb/dashboard')

View File

@@ -2,12 +2,11 @@ import { axios } from '@/utils/request'
const urlPrefix = '/v0.1'
export function searchCI(params, isShowMessage = true) {
export function searchCI(params) {
return axios({
url: urlPrefix + `/ci/s`,
method: 'GET',
params: params,
isShowMessage
params: params
})
}

View File

@@ -1,110 +0,0 @@
import { axios } from '@/utils/request'
const urlPrefix = '/v0.1'
export function getTopoGroups() {
return axios({
url: `${urlPrefix}/topology_views`,
method: 'GET',
})
}
export function getTopoView(_id) {
return axios({
url: `${urlPrefix}/topology_views/${_id}`,
method: 'GET',
})
}
export function postTopoGroup(data) {
return axios({
url: `${urlPrefix}/topology_views/groups`,
method: 'POST',
data: data
})
}
export function putTopoGroupByGId(gid, data) {
return axios({
url: `${urlPrefix}/topology_views/groups/${gid}`,
method: 'PUT',
data: data
})
}
export function putTopoGroupsOrder(data) {
return axios({
url: `${urlPrefix}/topology_views/groups/order`,
method: 'PUT',
data: data
})
}
export function deleteTopoGroup(gid, data) {
return axios({
url: `${urlPrefix}/topology_views/groups/${gid}`,
method: 'DELETE',
data: data
})
}
export function addTopoView(data) {
return axios({
url: `${urlPrefix}/topology_views`,
method: 'POST',
data: data
})
}
export function updateTopoView(_id, data) {
return axios({
url: `${urlPrefix}/topology_views/${_id}`,
method: 'PUT',
data: data
})
}
export function deleteTopoView(_id) {
return axios({
url: `${urlPrefix}/topology_views/${_id}`,
method: 'DELETE',
})
}
export function getRelationsByTypeId(_id) {
return axios({
url: `${urlPrefix}/topology_views/relations/ci_types/${_id}`,
method: 'GET',
})
}
export function previewTopoView(params) {
return axios({
url: `${urlPrefix}/topology_views/preview`,
method: 'POST',
data: params,
})
}
export function showTopoView(_id) {
return axios({
url: `${urlPrefix}/topology_views/${_id}/view`,
method: 'GET',
})
}
export function grantTopologyView(viewId, rid, data) {
return axios({
url: `/v0.1/topology_views/${viewId}/roles/${rid}/grant`,
method: 'POST',
data: data
})
}
export function revokeTopologyView(viewId, rid, data) {
return axios({
url: `/v0.1/topology_views/${viewId}/roles/${rid}/revoke`,
method: 'POST',
data: data
})
}

View File

@@ -57,20 +57,6 @@
:addedRids="addedRids"
/>
</template>
<template v-if="cmdbGrantType.includes('TopologyView')">
<div class="cmdb-grant-title">{{ resourceTypeName }}{{ $t('cmdb.components.perm') }}</div>
<TopologyViewGrant
:resourceTypeName="resourceTypeName"
:tableData="tableData"
:viewId="CITypeId"
grantType="TopologyView"
@grantDepart="grantDepart"
@grantRole="grantRole"
@getTableData="getTableData"
ref="grantTopologyView"
:addedRids="addedRids"
/>
</template>
<GrantModal ref="grantModal" @handleOk="handleOk" />
<ReadGrantModal ref="readGrantModal" :CITypeId="CITypeId" @updateTableDataRead="updateTableDataRead" />
@@ -86,12 +72,11 @@ import { getResourcePerms } from '@/modules/acl/api/permission'
import GrantModal from './grantModal.vue'
import ReadGrantModal from './readGrantModal'
import RelationViewGrant from './relationViewGrant.vue'
import TopologyViewGrant from './topologyViewGrant.vue'
import { getCITypeGroupById, ciTypeFilterPermissions } from '../../api/CIType'
export default {
name: 'GrantComp',
components: { CiTypeGrant, TypeRelationGrant, RelationViewGrant, TopologyViewGrant, GrantModal, ReadGrantModal },
components: { CiTypeGrant, TypeRelationGrant, RelationViewGrant, GrantModal, ReadGrantModal },
props: {
CITypeId: {
type: Number,
@@ -306,20 +291,6 @@ export default {
})
)
}
if (grantType === 'TopologyView') {
this.tableData.unshift(
...rids.map(({ rid, name }) => {
return {
rid,
name,
read: false,
update: false,
delete: false,
grant: false,
}
})
)
}
this.addedRids = rids
this.$nextTick(() => {
setTimeout(() => {

View File

@@ -1,102 +0,0 @@
<template>
<div class="topology-view-grant">
<vxe-table
ref="xTable"
size="mini"
stripe
class="ops-stripe-table"
:data="tableData"
:max-height="`${tableHeight}px`"
:scroll-y="{enabled: true}"
:row-style="(params) => getCurrentRowStyle(params, addedRids)"
>
<vxe-column field="name"></vxe-column>
<vxe-column v-for="col in columns" :key="col" :field="col" :title="permMap[col]">
<template #default="{row}">
<a-checkbox @change="(e) => handleChange(e, col, row)" v-model="row[col]"></a-checkbox>
</template>
</vxe-column>
</vxe-table>
<a-space>
<span class="grant-button" @click="grantDepart">{{ $t('cmdb.components.grantUser') }}</span>
<span class="grant-button" @click="grantRole">{{ $t('cmdb.components.grantRole') }}</span>
</a-space>
</div>
</template>
<script>
import { permMap } from './constants.js'
import { grantTopologyView, revokeTopologyView } from '@/modules/cmdb/api/topology.js'
import { getCurrentRowStyle } from './utils'
export default {
name: 'TopologyViewGrant',
inject: ['loading', 'isModal'],
props: {
viewId: {
type: Number,
default: null,
},
resourceTypeName: {
type: String,
default: '',
},
tableData: {
type: Array,
default: () => [],
},
grantType: {
type: String,
default: 'relation_view',
},
addedRids: {
type: Array,
default: () => [],
},
},
data() {
return {
columns: ['read', 'update', 'delete', 'grant'],
}
},
computed: {
windowHeight() {
return this.$store.state.windowHeight
},
tableHeight() {
if (this.isModal) {
return (this.windowHeight - 104) / 2
}
return (this.windowHeight - 104) / 2 - 116
},
permMap() {
return permMap()
}
},
methods: {
getCurrentRowStyle,
grantDepart() {
this.$emit('grantDepart', this.grantType)
},
grantRole() {
this.$emit('grantRole', this.grantType)
},
handleChange(e, col, row) {
if (e.target.checked) {
grantTopologyView(this.viewId, row.rid, { perms: [col], name: this.resourceTypeName }).catch(() => {
this.$emit('getTableData')
})
} else {
revokeTopologyView(this.viewId, row.rid, { perms: [col], name: this.resourceTypeName }).catch(() => {
this.$emit('getTableData')
})
}
},
},
}
</script>
<style lang="less" scoped>
.topology-view-grant {
padding: 10px 0;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -16,12 +16,6 @@ const genCmdbRoutes = async () => {
meta: { title: 'dashboard', icon: 'ops-cmdb-dashboard', selectedIcon: 'ops-cmdb-dashboard', keepAlive: false },
component: () => import('../views/dashboard/index_v2.vue')
},
{
path: '/cmdb/topoviews',
name: 'cmdb_topology_views',
meta: { title: 'cmdb.menu.topologyView', appName: 'cmdb', icon: 'ops-topology_view', selectedIcon: 'ops-topology_view', keepAlive: false },
component: () => import('../views/topology_view/index.vue')
},
{
path: '/cmdb/disabled1',
name: 'cmdb_disabled1',
@@ -149,30 +143,21 @@ const genCmdbRoutes = async () => {
}
// Dynamically add subscription items and business relationships
const [preference, relation] = await Promise.all([getPreference(), getRelationView()])
const resourceViewsIndex = routes.children.findIndex(item => item.name === 'cmdb_resource_views')
preference.group_types.forEach(group => {
if (preference.group_types.length > 1) {
routes.children[resourceViewsIndex].children.push({
path: `/cmdb/instances/types/group${group.id}`,
name: `cmdb_instances_group_${group.id}`,
meta: { title: group.name || 'other', disabled: true, style: 'margin-left: 12px' },
})
}
group.ci_types.forEach(item => {
routes.children[resourceViewsIndex].children.push({
path: `/cmdb/instances/types/${item.id}`,
component: () => import(`../views/ci/index`),
name: `cmdb_${item.id}`,
meta: { title: item.alias, keepAlive: false, typeId: item.id, name: item.name, customIcon: item.icon },
// hideChildrenInMenu: true // Force display of MenuItem instead of SubMenu
})
preference.forEach(item => {
routes.children[2].children.push({
path: `/cmdb/instances/types/${item.id}`,
component: () => import(`../views/ci/index`),
name: `cmdb_${item.id}`,
meta: { title: item.alias, keepAlive: false, typeId: item.id, name: item.name, customIcon: item.icon },
// hideChildrenInMenu: true // Force display of MenuItem instead of SubMenu
})
})
const lastTypeId = window.localStorage.getItem('ops_ci_typeid') || undefined
if (lastTypeId && preference.type_ids.some(item => item === Number(lastTypeId))) {
if (lastTypeId && preference.some(item => item.id === Number(lastTypeId))) {
routes.redirect = `/cmdb/instances/types/${lastTypeId}`
} else if (routes.children[resourceViewsIndex]?.children?.length > 0) {
routes.redirect = routes.children[resourceViewsIndex].children.find(item => !item.hidden && !item.meta.disabled)?.path
} else if (routes.children[2]?.children?.length > 0) {
routes.redirect = routes.children[2].children.find(item => !item.hidden)?.path
} else {
routes.redirect = '/cmdb/dashboard'
}
@@ -184,7 +169,7 @@ const genCmdbRoutes = async () => {
meta: { title: item[0], icon: 'ops-cmdb-relation', selectedIcon: 'ops-cmdb-relation', keepAlive: false, name: item[0] },
}
})
routes.children.splice(resourceViewsIndex, 0, ...relationViews)
routes.children.splice(2, 0, ...relationViews)
return routes
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="ci-types-wrap" :style="{ height: `${windowHeight - 96}px` }">
<div class="ci-types-wrap" :style="{ height: `${windowHeight - 66}px` }">
<div v-if="!CITypeGroups.length" class="ci-types-empty">
<a-empty :image="emptyImage" description=""></a-empty>
<a-button icon="plus" size="small" type="primary" @click="handleClickAddGroup">{{
@@ -15,13 +15,6 @@
:triggerLength="18"
>
<template #one>
<a-input
:placeholder="$t('cmdb.preference.searchPlaceholder')"
class="cmdb-ci-types-left-input"
@pressEnter="handleSearch"
>
<a-icon slot="prefix" type="search" />
</a-input>
<div class="ci-types-left">
<div class="ci-types-left-title">
<a-button
@@ -69,8 +62,8 @@
</a-dropdown>
</a-space>
</div>
<draggable class="ci-types-left-content" :list="computedCITypeGroups" @end="handleChangeGroups" filter=".undraggable">
<div v-for="g in computedCITypeGroups" :key="g.id || g.name">
<draggable class="ci-types-left-content" :list="CITypeGroups" @end="handleChangeGroups" filter=".undraggable">
<div v-for="g in CITypeGroups" :key="g.id || g.name">
<div
:class="
`${currentGId === g.id && !currentCId ? 'selected' : ''} ci-types-left-group ${
@@ -81,7 +74,6 @@
>
<div>
<OpsMoveIcon
v-if="g.id !== -1"
style="width: 17px; height: 17px; display: none; position: absolute; left: -3px; top: 10px"
/>
<span style="font-weight: 700">{{ g.name || $t('other') }}</span>
@@ -111,7 +103,6 @@
@start="start(g)"
@end="end(g)"
@add="add(g)"
filter=".undraggable"
>
<div
v-for="ci in g.ci_types"
@@ -119,9 +110,8 @@
:class="`${currentCId === ci.id ? 'selected' : ''} ci-types-left-detail`"
@click="handleClickCIType(g.id, ci.id, ci.name)"
>
<div :class="`${ g.id === -1 ? 'undraggable' : '' }`">
<div>
<OpsMoveIcon
v-if="g.id !== -1"
style="width: 17px; height: 17px; display: none; position: absolute; left: -1px; top: 8px"
/>
<span class="ci-types-left-detail-icon">
@@ -474,8 +464,6 @@ export default {
isInherit: false,
unique_id: null,
searchValue: '',
}
},
computed: {
@@ -548,16 +536,6 @@ export default {
}
return _showIdSelectOptions
},
computedCITypeGroups() {
if (this.searchValue) {
const ciTypes = _.cloneDeep(this.CITypeGroups)
ciTypes.forEach((item) => {
item.ci_types = item.ci_types.filter((_item) => _item.alias.toLowerCase().includes(this.searchValue.toLowerCase()))
})
return ciTypes
}
return this.CITypeGroups
},
},
provide() {
return {
@@ -587,9 +565,6 @@ export default {
this.allTreeDepAndEmp = res
})
},
handleSearch(e) {
this.searchValue = e.target.value
},
async loadCITypes(isResetCurrentId = false) {
const groups = await getCITypeGroupsConfig({ need_other: true })
let alreadyReset = false
@@ -689,7 +664,7 @@ export default {
content: that.$t('cmdb.ciType.confirmDeleteGroup', { groupName: `${g.name}` }),
onOk() {
deleteCITypeGroup(g.id).then((res) => {
that.$message.success(that.$t('deleteSuccess'))
that.$message.info(that.$t('deleteSuccess'))
that.loadCITypes(true)
})
},
@@ -1046,14 +1021,6 @@ export default {
top: 40%;
transform: translate(-50%, -50%);
}
/deep/.cmdb-ci-types-left-input {
input {
background-color: transparent;
}
.ant-input:focus {
box-shadow: none;
}
}
.ci-types-left {
width: 100%;
overflow: auto;

View File

@@ -1,5 +1,5 @@
<template>
<div class="relation-table" :style="{ padding: '0 20px 20px' }">
<div :style="{ padding: '0 20px 20px' }">
<a-button
v-if="!isInGrantComp"
style="margin-bottom: 10px"
@@ -18,7 +18,6 @@
highlight-hover-row
keep-source
class="ops-stripe-table"
min-height="500"
:row-class-name="rowClass"
:edit-config="{ trigger: 'dblclick', mode: 'cell', showIcon: false }"
resizable
@@ -44,7 +43,7 @@
<span v-else>{{ constraintMap[row.constraint] }}</span>
</template>
</vxe-column>
<vxe-column :width="300" field="attributeAssociation" :edit-render="{}">
<vxe-column :width="250" field="attributeAssociation" :edit-render="{}">
<template #header>
<span>
<a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')">
@@ -57,73 +56,43 @@
</span>
</template>
<template #default="{row}">
<template
v-for="item in row.parentAndChildAttrList"
<span
v-if="row.parent_attr_id && row.child_attr_id"
>{{ getAttrNameById(row.isParent ? row.attributes : attributes, row.parent_attr_id) }}=>
{{ getAttrNameById(row.isParent ? attributes : row.attributes, row.child_attr_id) }}</span
>
<div
:key="item.id"
v-if="item.parentAttrId && item.childAttrId"
>
{{ getAttrNameById(row.isParent ? row.attributes : attributes, item.parentAttrId) }}=>
{{ getAttrNameById(row.isParent ? attributes : row.attributes, item.childAttrId) }}
</div>
</template>
</template>
<template #edit="{ row }">
<div
v-for="item in tableAttrList"
:key="item.id"
class="table-attribute-row"
>
<div style="display:inline-flex;align-items:center;">
<a-select
allowClear
size="small"
v-model="item.parentAttrId"
v-model="parent_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
show-search
optionFilterProp="title"
>
<a-select-option
<a-select-option
v-for="attr in filterAttributes(row.isParent ? row.attributes : attributes)"
:key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
>
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
<span class="table-attribute-row-link">=></span>
=>
<a-select
allowClear
size="small"
v-model="item.childAttrId"
v-model="child_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
show-search
optionFilterProp="title"
>
<a-select-option
v-for="attr in filterAttributes(row.isParent ? attributes : row.attributes)"
:key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
>
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
<a
class="table-attribute-row-action"
@click="removeTableAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="table-attribute-row-action"
@click="addTableAttr"
>
<a-icon type="plus-circle" />
</a>
</div>
</template>
</vxe-column>
@@ -210,16 +179,13 @@
</a-select>
</a-form-item>
<a-form-item :label="$t('cmdb.ciType.attributeAssociation')">
<a-row
v-for="item in modalAttrList"
:key="item.id"
>
<a-col :span="10">
<a-row>
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip4')"
allowClear
v-model="item.parentAttrId"
v-decorator="['parent_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(attributes)" :key="attr.id">
{{ attr.alias || attr.name }}
@@ -230,12 +196,12 @@
<a-col :span="2" :style="{ textAlign: 'center' }">
=>
</a-col>
<a-col :span="9">
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip5')"
allowClear
v-model="item.childAttrId"
v-decorator="['child_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id">
{{ attr.alias || attr.name }}
@@ -243,20 +209,6 @@
</a-select>
</a-form-item>
</a-col>
<a-col :span="3">
<a
class="modal-attribute-action"
@click="removeModalAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="modal-attribute-action"
@click="addModalAttr"
>
<a-icon type="plus-circle" />
</a>
</a-col>
</a-row>
</a-form-item>
</a-form>
@@ -275,7 +227,6 @@ import {
} from '@/modules/cmdb/api/CITypeRelation'
import { getCITypes } from '@/modules/cmdb/api/CIType'
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
import { v4 as uuidv4 } from 'uuid'
import CMDBGrant from '../../components/cmdbGrant'
@@ -308,8 +259,8 @@ export default {
tableData: [],
parentTableData: [],
attributes: [],
tableAttrList: [],
modalAttrList: [],
parent_attr_id: undefined,
child_attr_id: undefined,
modalChildAttributes: [],
}
},
@@ -346,11 +297,8 @@ export default {
async getCITypeParent() {
await getCITypeParent(this.CITypeId).then((res) => {
this.parentTableData = res.parents.map((item) => {
const parentAndChildAttrList = this.handleAttrList(item)
return {
...item,
parentAndChildAttrList,
source_ci_type_name: this.CITypeName,
source_ci_type_id: this.CITypeId,
isParent: true,
@@ -361,11 +309,8 @@ export default {
getCITypeChildren() {
getCITypeChildren(this.CITypeId).then((res) => {
const data = res.children.map((obj) => {
const parentAndChildAttrList = this.handleAttrList(obj)
return {
...obj,
parentAndChildAttrList,
source_ci_type_name: this.CITypeName,
source_ci_type_id: this.CITypeId,
}
@@ -377,20 +322,6 @@ export default {
}
})
},
handleAttrList(data) {
const length = Math.min(data?.parent_attr_ids?.length || 0, data.child_attr_ids?.length || 0)
const parentAndChildAttrList = []
for (let i = 0; i < length; i++) {
parentAndChildAttrList.push({
id: uuidv4(),
parentAttrId: data?.parent_attr_ids?.[i] ?? '',
childAttrId: data?.child_attr_ids?.[i] ?? ''
})
}
return parentAndChildAttrList
},
getCITypes() {
getCITypes().then((res) => {
this.CITypes = res.ci_types
@@ -411,13 +342,6 @@ export default {
handleCreate() {
this.drawerTitle = this.$t('cmdb.ciType.addRelation')
this.visible = true
this.$set(this, 'modalAttrList', [
{
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
}
])
this.$nextTick(() => {
this.form.setFieldsValue({
source_ci_type_id: this.CITypeId,
@@ -441,22 +365,19 @@ export default {
ci_type_id,
relation_type_id,
constraint,
parent_attr_id = undefined,
child_attr_id = undefined,
} = values
const {
parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.modalAttrList)
if (!validate) {
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return
}
createRelation(source_ci_type_id, ci_type_id, {
relation_type_id,
constraint,
parent_attr_ids,
child_attr_ids,
parent_attr_id,
child_attr_id,
}).then((res) => {
this.$message.success(this.$t('addSuccess'))
this.onClose()
@@ -465,37 +386,6 @@ export default {
}
})
},
/**
* 校验属性列表
* @param {*} attrList
*/
handleValidateAttrList(attrList) {
const parent_attr_ids = []
const child_attr_ids = []
attrList.map((attr) => {
if (attr.parentAttrId) {
parent_attr_ids.push(attr.parentAttrId)
}
if (attr.childAttrId) {
child_attr_ids.push(attr.childAttrId)
}
})
if (parent_attr_ids.length !== child_attr_ids.length) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return {
validate: false
}
}
return {
validate: true,
parent_attr_ids,
child_attr_ids
}
},
handleOpenGrant(record) {
this.$refs.cmdbGrant.open({
name: `${record.source_ci_type_name} -> ${record.name}`,
@@ -511,45 +401,23 @@ export default {
if (row.isParent) return 'relation-table-parent'
},
handleEditActived({ row }) {
const tableAttrList = []
const length = Math.min(row?.parent_attr_ids?.length || 0, row.child_attr_ids?.length || 0)
if (length) {
for (let i = 0; i < length; i++) {
tableAttrList.push({
id: uuidv4(),
parentAttrId: row?.parent_attr_ids?.[i] ?? undefined,
childAttrId: row?.child_attr_ids?.[i] ?? undefined
})
}
} else {
tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
}
this.$set(this, 'tableAttrList', tableAttrList)
this.parent_attr_id = row?.parent_attr_id ?? undefined
this.child_attr_id = row?.child_attr_id ?? undefined
},
async handleEditClose({ row }) {
const { source_ci_type_id: parentId, id: childrenId, constraint, relation_type } = row
const { parent_attr_id, child_attr_id } = this
const _find = this.relationTypes.find((item) => item.name === relation_type)
const relation_type_id = _find?.id
const {
parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.tableAttrList)
if (!validate) {
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return
}
await createRelation(row.isParent ? childrenId : parentId, row.isParent ? parentId : childrenId, {
relation_type_id,
constraint,
parent_attr_ids,
child_attr_ids,
parent_attr_id,
child_attr_id,
}).finally(() => {
this.getData()
})
@@ -559,9 +427,7 @@ export default {
return _find?.alias ?? _find?.name ?? id
},
changeChild(value) {
this.modalAttrList.forEach((item) => {
item.childAttrId = undefined
})
this.form.setFieldsValue({ child_attr_id: undefined })
getCITypeAttributesById(value).then((res) => {
this.modalChildAttributes = res?.attributes ?? []
})
@@ -570,75 +436,10 @@ export default {
// filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
},
addTableAttr() {
this.tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeTableAttr(id) {
if (this.tableAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.tableAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.tableAttrList.splice(index, 1)
}
},
addModalAttr() {
this.modalAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeModalAttr(id) {
if (this.modalAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.modalAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.modalAttrList.splice(index, 1)
}
}
},
}
</script>
<style lang="less" scoped>
.relation-table {
/deep/ .vxe-cell {
max-height: max-content !important;
}
}
.table-attribute-row {
display: inline-flex;
align-items: center;
margin-top: 5px;
&:last-child {
margin-bottom: 5px;
}
&-link {
margin: 0 5px;
}
&-action {
margin-left: 5px;
}
}
.modal-attribute-action {
margin-left: 5px;
}
</style>
<style lang="less">
.ops-stripe-table .vxe-body--row.row--stripe.relation-table-divider {
background-color: #b1b8d3 !important;

View File

@@ -70,16 +70,13 @@
</a-select>
</a-form-item>
<a-form-item :label="$t('cmdb.ciType.attributeAssociation')">
<a-row
v-for="item in modalAttrList"
:key="item.id"
>
<a-col :span="10">
<a-row>
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip4')"
allowClear
v-model="item.parentAttrId"
v-decorator="['parent_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(modalParentAttributes)" :key="attr.id">
{{ attr.alias || attr.name }}
@@ -90,12 +87,12 @@
<a-col :span="2" :style="{ textAlign: 'center' }">
=>
</a-col>
<a-col :span="9">
<a-col :span="11">
<a-form-item>
<a-select
:placeholder="$t('cmdb.ciType.attributeAssociationTip5')"
allowClear
v-model="item.childAttrId"
v-decorator="['child_attr_id', { rules: [{ required: false }] }]"
>
<a-select-option v-for="attr in filterAttributes(modalChildAttributes)" :key="attr.id">
{{ attr.alias || attr.name }}
@@ -103,20 +100,6 @@
</a-select>
</a-form-item>
</a-col>
<a-col :span="3">
<a
class="modal-attribute-action"
@click="removeModalAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="modal-attribute-action"
@click="addModalAttr"
>
<a-icon type="plus-circle" />
</a>
</a-col>
</a-row>
</a-form-item>
</a-form>
@@ -131,7 +114,6 @@ import { getCITypeGroupsConfig } from '@/modules/cmdb/api/ciTypeGroup'
import { getCITypes } from '@/modules/cmdb/api/CIType'
import { createRelation, deleteRelation, getCITypeChildren, getRelationTypes } from '@/modules/cmdb/api/CITypeRelation'
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
import { v4 as uuidv4 } from 'uuid'
export default {
name: 'Index',
@@ -157,7 +139,6 @@ export default {
modalParentAttributes: [],
modalChildAttributes: [],
modalAttrList: [],
}
},
computed: {
@@ -247,13 +228,6 @@ export default {
handleCreate() {
this.drawerTitle = this.$t('cmdb.ciType.addRelation')
this.visible = true
this.$set(this, 'modalAttrList', [
{
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
}
])
this.$nextTick(() => {
this.form.setFieldsValue({
source_ci_type_id: this.sourceCITypeId,
@@ -275,22 +249,19 @@ export default {
ci_type_id,
relation_type_id,
constraint,
parent_attr_id = undefined,
child_attr_id = undefined,
} = values
const {
parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.modalAttrList)
if (!validate) {
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return
}
createRelation(source_ci_type_id, ci_type_id, {
relation_type_id,
constraint,
parent_attr_ids,
child_attr_ids,
parent_attr_id,
child_attr_id,
}).then((res) => {
this.$message.success(this.$t('addSuccess'))
this.onClose()
@@ -301,37 +272,6 @@ export default {
this.sourceCITypeId = undefined
this.targetCITypeId = undefined
},
/**
* 校验属性列表
* @param {*} attrList
*/
handleValidateAttrList(attrList) {
const parent_attr_ids = []
const child_attr_ids = []
attrList.map((attr) => {
if (attr.parentAttrId) {
parent_attr_ids.push(attr.parentAttrId)
}
if (attr.childAttrId) {
child_attr_ids.push(attr.childAttrId)
}
})
if (parent_attr_ids.length !== child_attr_ids.length) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return {
validate: false
}
}
return {
validate: true,
parent_attr_ids,
child_attr_ids
}
},
handleOk() {
this.$refs.table.refresh()
},
@@ -344,18 +284,14 @@ export default {
},
handleSourceTypeChange(value) {
this.sourceCITypeId = value
this.modalAttrList.forEach((item) => {
item.parentAttrId = undefined
})
this.form.setFieldsValue({ parent_attr_id: undefined })
getCITypeAttributesById(value).then((res) => {
this.modalParentAttributes = res?.attributes ?? []
})
},
handleTargetTypeChange(value) {
this.targetCITypeId = value
this.modalAttrList.forEach((item) => {
item.childAttrId = undefined
})
this.form.setFieldsValue({ child_attr_id: undefined })
getCITypeAttributesById(value).then((res) => {
this.modalChildAttributes = res?.attributes ?? []
})
@@ -367,30 +303,12 @@ export default {
// filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
},
addModalAttr() {
this.modalAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeModalAttr(id) {
if (this.modalAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.modalAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.modalAttrList.splice(index, 1)
}
}
},
}
</script>
<style lang="less" scoped>
.model-relation {
background-color: #fff;
border-radius: @border-radius-box;
@@ -398,8 +316,4 @@ export default {
height: calc(100vh - 64px);
margin-bottom: -24px;
}
.modal-attribute-action {
margin-left: 5px;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="model-relation-table">
<div>
<vxe-table
ref="xTable"
stripe
@@ -7,7 +7,6 @@
show-header-overflow
show-overflow
resizable
:scroll-y="{enabled: false}"
:height="`${windowHeight - 160}px`"
:data="tableData"
:sort-config="{ defaultSort: { field: 'created_at', order: 'desc' } }"
@@ -35,7 +34,7 @@
{{ handleConstraint(row.constraint) }}
</template>
</vxe-column>
<vxe-column :width="300" field="attributeAssociation" :edit-render="{}">
<vxe-column :width="250" field="attributeAssociation" :edit-render="{}">
<template #header>
<span>
<a-tooltip :title="$t('cmdb.ciType.attributeAssociationTip1')">
@@ -48,73 +47,37 @@
</span>
</template>
<template #default="{row}">
<template
v-for="item in row.parentAndChildAttrList"
<span
v-if="row.parent_attr_id && row.child_attr_id"
>{{ getAttrNameById(type2attributes[row.parent_id], row.parent_attr_id) }}=>
{{ getAttrNameById(type2attributes[row.child_id], row.child_attr_id) }}</span
>
<div
:key="item.id"
v-if="item.parentAttrId && item.childAttrId"
>
{{ getAttrNameById(type2attributes[row.parent_id], item.parentAttrId) }}=>
{{ getAttrNameById(type2attributes[row.child_id], item.childAttrId) }}
</div>
</template>
</template>
<template #edit="{ row }">
<div
v-for="item in tableAttrList"
:key="item.id"
class="table-attribute-row"
>
<div style="display:inline-flex;align-items:center;">
<a-select
allowClear
size="small"
v-model="item.parentAttrId"
v-model="parent_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
show-search
optionFilterProp="title"
>
<a-select-option
v-for="attr in filterAttributes(type2attributes[row.parent_id])"
:key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
>
<a-select-option v-for="attr in filterAttributes(type2attributes[row.parent_id])" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
<span class="table-attribute-row-link">=></span>
=>
<a-select
allowClear
size="small"
v-model="item.childAttrId"
v-model="child_attr_id"
:getPopupContainer="(trigger) => trigger.parentNode"
:style="{ width: '100px' }"
show-search
optionFilterProp="title"
>
<a-select-option
v-for="attr in filterAttributes(type2attributes[row.child_id])"
:key="attr.id"
:value="attr.id"
:title="attr.alias || attr.name"
>
<a-select-option v-for="attr in filterAttributes(type2attributes[row.child_id])" :key="attr.id">
{{ attr.alias || attr.name }}
</a-select-option>
</a-select>
<a
class="table-attribute-row-action"
@click="removeTableAttr(item.id)"
>
<a-icon type="minus-circle" />
</a>
<a
class="table-attribute-row-action"
@click="addTableAttr"
>
<a-icon type="plus-circle" />
</a>
</div>
</template>
</vxe-column>
@@ -134,7 +97,6 @@
</template>
<script>
import { v4 as uuidv4 } from 'uuid'
import { getCITypeRelations, deleteRelation, createRelation } from '@/modules/cmdb/api/CITypeRelation'
import { getRelationTypes } from '@/modules/cmdb/api/relationType'
import CMDBGrant from '../../../components/cmdbGrant'
@@ -146,7 +108,8 @@ export default {
tableData: [],
relationTypeList: null,
type2attributes: {},
tableAttrList: [],
parent_attr_id: undefined,
child_attr_id: undefined,
}
},
components: {
@@ -174,29 +137,9 @@ export default {
},
async getMainData() {
const { relations, type2attributes } = await getCITypeRelations()
this.tableData = relations.map((item) => {
const parentAndChildAttrList = this.handleAttrList(item)
return {
...item,
parentAndChildAttrList
}
})
this.tableData = relations
this.type2attributes = type2attributes
},
handleAttrList(data) {
const length = Math.min(data?.parent_attr_ids?.length || 0, data.child_attr_ids?.length || 0)
const parentAndChildAttrList = []
for (let i = 0; i < length; i++) {
parentAndChildAttrList.push({
id: uuidv4(),
parentAttrId: data?.parent_attr_ids?.[i] ?? '',
childAttrId: data?.child_attr_ids?.[i] ?? ''
})
}
return parentAndChildAttrList
},
// 获取关系
async getRelationTypes() {
const res = await getRelationTypes()
@@ -228,75 +171,21 @@ export default {
})
},
handleEditActived({ row }) {
const tableAttrList = []
const length = Math.min(row?.parent_attr_ids?.length || 0, row.child_attr_ids?.length || 0)
if (length) {
for (let i = 0; i < length; i++) {
tableAttrList.push({
id: uuidv4(),
parentAttrId: row?.parent_attr_ids?.[i] ?? undefined,
childAttrId: row?.child_attr_ids?.[i] ?? undefined
})
}
} else {
tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
}
console.log('handleEditActived', tableAttrList)
this.$set(this, 'tableAttrList', tableAttrList)
this.parent_attr_id = row?.parent_attr_id ?? undefined
this.child_attr_id = row?.child_attr_id ?? undefined
},
/**
* 校验属性列表
* @param {*} attrList
*/
handleValidateAttrList(attrList) {
const parent_attr_ids = []
const child_attr_ids = []
attrList.map((attr) => {
if (attr.parentAttrId) {
parent_attr_ids.push(attr.parentAttrId)
}
if (attr.childAttrId) {
child_attr_ids.push(attr.childAttrId)
}
})
if (parent_attr_ids.length !== child_attr_ids.length) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return {
validate: false
}
}
return {
validate: true,
parent_attr_ids,
child_attr_ids
}
},
async handleEditClose({ row }) {
const { parent_id, child_id, constraint, relation_type_id } = row
const {
parent_attr_ids,
child_attr_ids,
validate
} = this.handleValidateAttrList(this.tableAttrList)
if (!validate) {
const { parent_attr_id = undefined, child_attr_id = undefined } = this
if ((!parent_attr_id && child_attr_id) || (parent_attr_id && !child_attr_id)) {
this.$message.warning(this.$t('cmdb.ciType.attributeAssociationTip3'))
return
}
await createRelation(parent_id, child_id, {
relation_type_id,
constraint,
parent_attr_ids,
child_attr_ids,
parent_attr_id,
child_attr_id,
}).finally(() => {
this.getMainData()
})
@@ -309,49 +198,8 @@ export default {
// filter password/json/is_list
return attributes.filter((attr) => !attr.is_password && !attr.is_list && attr.value_type !== '6')
},
addTableAttr() {
this.tableAttrList.push({
id: uuidv4(),
parentAttrId: undefined,
childAttrId: undefined
})
},
removeTableAttr(id) {
if (this.tableAttrList.length <= 1) {
this.$message.error(this.$t('cmdb.ciType.attributeAssociationTip6'))
return
}
const index = this.tableAttrList.findIndex((item) => item.id === id)
if (index !== -1) {
this.tableAttrList.splice(index, 1)
}
},
},
}
</script>
<style lang="less" scoped>
.relation-table {
/deep/ .vxe-cell {
max-height: max-content !important;
}
}
.table-attribute-row {
display: inline-flex;
align-items: center;
margin-top: 5px;
&:last-child {
margin-bottom: 5px;
}
&-link {
margin: 0 5px;
}
&-action {
margin-left: 5px;
}
}
</style>
<style></style>

View File

@@ -32,77 +32,62 @@
}"
/></span>
</div>
<div class="cmdb-preference-group" v-for="(subType, index) in myPreferences" :key="subType.name">
<div class="cmdb-preference-group" v-for="(group, index) in myPreferences" :key="group.name">
<div class="cmdb-preference-group-title">
<span> <ops-icon :style="{ marginRight: '10px' }" :type="subType.icon" />{{ subType.name }}</span>
<span> <ops-icon :style="{ marginRight: '10px' }" :type="group.icon" />{{ group.name }} </span>
</div>
<draggable class="ci-types-left-content" :list="subType.groups" @end="handleChangeGroups" filter=".undraggable">
<div v-for="group in subType.groups" :key="group.id || group.name">
<draggable
v-model="group.ci_types"
:animation="300"
@change="
(e) => {
orderChange(e, group)
}
"
>
<div class="cmdb-preference-group-content" v-for="ciType in group.ci_types" :key="ciType.id">
<OpsMoveIcon class="cmdb-preference-move-icon" />
<div
:class="
`${group.id === undefined ? 'undraggable' : ''}`
"
:class="{
'cmdb-preference-avatar': true,
'cmdb-preference-avatar-noicon': !ciType.icon,
}"
:style="{ width: '30px', height: '30px', marginRight: '10px' }"
>
<div v-if="index === 0 && subType.groups.length > 1" class="cmdb-preference-group-content">
<OpsMoveIcon class="cmdb-preference-move-icon" v-if="group.name"/>
<span style="font-weight: 500; color: #a5a9bc"><ellipsis :length="25" tooltip>{{ group.name || $t('other') }}</ellipsis></span>
<span :style="{ color: '#c3cdd7' }">({{ group.ci_types.length }})</span>
</div>
</div>
<draggable
v-model="group.ci_types"
:animation="300"
@change="
(e) => {
orderChange(e, group, index === 1)
}
"
>
<div class="cmdb-preference-group-content" v-for="ciType in group.ci_types" :key="ciType.id">
<OpsMoveIcon class="cmdb-preference-move-icon" />
<div
:class="{
'cmdb-preference-avatar': true,
'cmdb-preference-avatar-noicon': !ciType.icon,
<template v-if="ciType.icon">
<img
v-if="ciType.icon.split('$$')[2]"
:src="`/api/common-setting/v1/file/${ciType.icon.split('$$')[3]}`"
:style="{ maxHeight: '30px', maxWidth: '30px' }"
/>
<ops-icon
v-else
:style="{
color: ciType.icon.split('$$')[1],
fontSize: '14px',
}"
:style="{ width: '30px', height: '30px', marginRight: '10px' }"
>
<template v-if="ciType.icon">
<img
v-if="ciType.icon.split('$$')[2]"
:src="`/api/common-setting/v1/file/${ciType.icon.split('$$')[3]}`"
:style="{ maxHeight: '30px', maxWidth: '30px' }"
/>
<ops-icon
v-else
:style="{
color: ciType.icon.split('$$')[1],
fontSize: '14px',
}"
:type="ciType.icon.split('$$')[0]"
/>
</template>
<span v-else :style="{ fontSize: '20px' }">{{ ciType.name[0].toUpperCase() }}</span>
</div>
<span class="cmdb-preference-group-content-title">{{ ciType.alias || ciType.name }}</span>
<span class="cmdb-preference-group-content-action">
<a-tooltip :title="$t('cmdb.preference.cancelSub')">
<span
@click="unsubscribe(ciType, group.type)"
><ops-icon type="cmdb-preference-cancel-subscribe" />
</span>
</a-tooltip>
<a-divider type="vertical" :style="{ margin: '0 3px' }" />
<a-tooltip :title="$t('cmdb.preference.editSub')">
<span
@click="openSubscribeSetting(ciType, `${index + 1}`)"
><ops-icon
type="cmdb-preference-subscribe"
/></span>
</a-tooltip>
:type="ciType.icon.split('$$')[0]"
/>
</template>
<span v-else :style="{ fontSize: '20px' }">{{ ciType.name[0].toUpperCase() }}</span>
</div>
<span class="cmdb-preference-group-content-title">{{ ciType.alias || ciType.name }}</span>
<span class="cmdb-preference-group-content-action">
<a-tooltip :title="$t('cmdb.preference.cancelSub')">
<span
@click="unsubscribe(ciType, group.type)"
><ops-icon type="cmdb-preference-cancel-subscribe" />
</span>
</div>
</draggable>
</a-tooltip>
<a-divider type="vertical" :style="{ margin: '0 3px' }" />
<a-tooltip :title="$t('cmdb.preference.editSub')">
<span
@click="openSubscribeSetting(ciType, `${index + 1}`)"
><ops-icon
type="cmdb-preference-subscribe"
/></span>
</a-tooltip>
</span>
</div>
</draggable>
</div>
@@ -212,7 +197,6 @@ import store from '@/store'
import { mapState } from 'vuex'
import moment from 'moment'
import draggable from 'vuedraggable'
import { Ellipsis } from '@/components'
import { getCITypeGroups } from '../../api/ciTypeGroup'
import {
getPreference,
@@ -228,7 +212,7 @@ import { ops_move_icon as OpsMoveIcon } from '@/core/icons'
export default {
name: 'Preference',
components: { CollapseTransition, SubscribeSetting, draggable, OpsMoveIcon, Ellipsis },
components: { CollapseTransition, SubscribeSetting, draggable, OpsMoveIcon },
data() {
return {
citypeData: [],
@@ -277,7 +261,7 @@ export default {
ciTypeGroup.forEach((group) => {
if (group.ci_types && group.ci_types.length) {
group.ci_types.forEach((type) => {
const idx = pref.type_ids.findIndex((p) => p === type.id)
const idx = pref.findIndex((p) => p.id === type.id)
if (idx > -1) {
type.is_subscribed = true
}
@@ -299,18 +283,19 @@ export default {
const _myPreferences = [
{
name: this.$t('cmdb.menu.ciTable'),
groups: pref.group_types,
ci_types: self.instance.map((item) => {
const _find = pref.find((ci) => ci.id === item)
return _find
}),
icon: 'cmdb-ci',
type: 'ci',
},
{
name: this.$t('cmdb.menu.ciTree'),
groups: [
{
ci_types: pref.tree_types,
name: null,
}
],
ci_types: self.tree.map((item) => {
const _find = pref.find((ci) => ci.id === item)
return _find
}),
icon: 'cmdb-tree',
type: 'tree',
},
@@ -392,41 +377,10 @@ export default {
this.expandKeys.push(group.id)
}
},
async handleChangeGroups() {
const typeIds = []
this.myPreferences[0].groups.forEach(groupTypes => {
groupTypes.ci_types.forEach(ciType => {
typeIds.push(ciType.id)
})
})
preferenceCitypeOrder({ type_ids: typeIds, is_tree: false })
orderChange(e, group) {
preferenceCitypeOrder({ type_ids: group.ci_types.map((type) => type.id), is_tree: group.type !== 'ci' })
.then(() => {
this.resetRoute()
})
.catch(() => {
this.getCITypes(false)
})
},
orderChange(e, group, isTree) {
let typeIds = []
if (!isTree) {
this.myPreferences[0].groups.forEach(groupTypes => {
if (group.id === groupTypes.id) {
group.ci_types.forEach(ciType => {
typeIds.push(ciType.id)
})
} else {
groupTypes.ci_types.forEach(ciType => {
typeIds.push(ciType.id)
})
}
})
} else {
typeIds = group.ci_types.map(item => item.id)
}
preferenceCitypeOrder({ type_ids: typeIds, is_tree: isTree })
.then(() => {
if (!isTree) {
if (group.type === 'ci') {
this.resetRoute()
}
})
@@ -488,23 +442,6 @@ export default {
margin-bottom: 20px;
}
.cmdb-preference-group {
.ci-types-left-content {
max-height: calc(100% - 45px);
overflow: hidden;
&:hover {
overflow: auto;
}
.undraggable{
.cmdb-preference-group-content {
cursor: default;
margin-left: 20px;
&:hover {
background: transparent;
box-shadow: none;
}
}
}
}
.cmdb-preference-group-title {
text-align: center;
margin-bottom: 5px;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,21 @@
version: '2.19'
version: '3.5'
services:
cmdb-db:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-db:2.3
container_name: cmdb-db
env_file:
- .env
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: '123456'
MYSQL_DATABASE: 'cmdb'
MYSQL_USER: 'cmdb'
MYSQL_PASSWORD: '123456'
volumes:
- db-data:/var/lib/mysql
- ./docs/mysqld.cnf:/etc/mysql/conf.d/mysqld.cnf
- ./docs/cmdb.sql:/docker-entrypoint-initdb.d/cmdb.sql
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost", "-p$MYSQL_ROOT_PASSWORD"]
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
@@ -43,10 +45,11 @@ services:
- redis
cmdb-api:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.5
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-api:2.4.4
# build:
# context: .
# target: cmdb-api
container_name: cmdb-api
env_file:
- .env
environment:
TZ: Asia/Shanghai
WAIT_HOSTS: cmdb-db:3306, cmdb-cache:6379
@@ -74,13 +77,17 @@ services:
flask init-import-user-from-acl
flask init-department
flask cmdb-counter > counter.log 2>&1
networks:
new:
aliases:
- cmdb-api
cmdb-ui:
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.5
image: registry.cn-hangzhou.aliyuncs.com/veops/cmdb-ui:2.4.4
# build:
# context: .
# target: cmdb-ui
container_name: cmdb-ui
depends_on:
- cmdb-api

View File

@@ -5,31 +5,14 @@
## Install
- 启动 mysql 服务, redis 服务,此处以 docker 为例
```bash
mkdir ~/cmdb_db # 用于持久化存储mysql数据
docker run -d -p 3306:3306 --name mysql-cmdb -e MYSQL_ROOT_PASSWORD=Root_321 -v ~/cmdb_db:/var/lib/mysql mysql
docker run -d --name redis -p 6379:6379 redis
```
- mysql需要先设置sql_mode, 进入容器,使用root账号,进入mysql执行:
```bash
docker exec -it mysql-cmdb bash
mysql -uroot -pRoot_321
```
```sql
`set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';`
`set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';`
```
- 启动 mysql 服务, redis 服务
> mysql一定要设置sql_mode, root进入mysql执行:
>
> `set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';`
>
> `set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';`
- 创建数据库 cmdb
```sql
create database cmdb;
```
- 拉取代码
```bash
@@ -43,8 +26,7 @@ cp cmdb-api/settings.example.py cmdb-api/settings.py
- 安装库
- 后端: `cd cmdb-api && pipenv run pipenv install && cd ..`
- 前端: `cd cmdb-ui && yarn install && cd ..`
- node推荐使用14.x版本,推荐使用nvm进行nodejs版本管理`nvm install 14 && nvm use 14`
- 可以将 docs/cmdb.sql 导入到数据库里,登录用户和密码分别是:demo/123456
- 可以将 docs/cmdb.sql 导入到数据库里,登录用户和密码分别是:demo/123456
- 创建数据库表: 进入**cmdb-api**目录执行 `pipenv run flask db-setup && pipenv run flask common-check-new-columns && pipenv run flask cmdb-init-cache`
- 启动服务