From dca8781a0355175a405984214716ebeff581c40d Mon Sep 17 00:00:00 2001 From: pycook Date: Mon, 25 Sep 2023 15:46:07 +0800 Subject: [PATCH 1/3] fix: ci_cache --- cmdb-api/api/tasks/cmdb.py | 67 ++++++++++++-------------------------- 1 file changed, 20 insertions(+), 47 deletions(-) diff --git a/cmdb-api/api/tasks/cmdb.py b/cmdb-api/api/tasks/cmdb.py index 491fe1c..34aa315 100644 --- a/cmdb-api/api/tasks/cmdb.py +++ b/cmdb-api/api/tasks/cmdb.py @@ -4,8 +4,6 @@ import json import time -import jinja2 -import requests from flask import current_app from flask_login import login_user @@ -18,7 +16,6 @@ from api.lib.cmdb.cache import CITypeAttributesCache from api.lib.cmdb.const import CMDB_QUEUE from api.lib.cmdb.const import REDIS_PREFIX_CI from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION -from api.lib.mail import send_mail from api.lib.perm.acl.cache import UserCache from api.lib.utils import Lock from api.lib.utils import handle_arg_list @@ -28,7 +25,9 @@ from api.models.cmdb import CITypeAttribute @celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE) -def ci_cache(ci_id): +def ci_cache(ci_id, operate_type, record_id): + from api.lib.cmdb.ci import CITriggerManager + time.sleep(0.01) db.session.remove() @@ -42,9 +41,14 @@ def ci_cache(ci_id): current_app.logger.info("{0} flush..........".format(ci_id)) + current_app.test_request_context().push() + login_user(UserCache.get('worker')) + + CITriggerManager.fire(operate_type, ci_dict, record_id) + @celery.task(name="cmdb.batch_ci_cache", queue=CMDB_QUEUE) -def batch_ci_cache(ci_ids): +def batch_ci_cache(ci_ids, ): # only for attribute change index time.sleep(1) db.session.remove() @@ -61,7 +65,10 @@ def batch_ci_cache(ci_ids): @celery.task(name="cmdb.ci_delete", queue=CMDB_QUEUE) -def ci_delete(ci_id): +def ci_delete(ci_dict, operate_type, record_id): + from api.lib.cmdb.ci import CITriggerManager + + ci_id = ci_dict.get('_id') current_app.logger.info(ci_id) if current_app.config.get("USE_ES"): @@ -71,6 +78,11 @@ def ci_delete(ci_id): current_app.logger.info("{0} delete..........".format(ci_id)) + current_app.test_request_context().push() + login_user(UserCache.get('worker')) + + CITriggerManager.fire(operate_type, ci_dict, record_id) + @celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE) def ci_relation_cache(parent_id, child_id): @@ -168,46 +180,6 @@ def ci_type_attribute_order_rebuild(type_id): order += 1 -@celery.task(name='cmdb.trigger_notify', queue=CMDB_QUEUE) -def trigger_notify(notify, ci_id): - from api.lib.perm.acl.cache import UserCache - - def _wrap_mail(mail_to): - if "@" not in mail_to: - user = UserCache.get(mail_to) - if user: - return user.email - - return mail_to - - db.session.remove() - - m = api.lib.cmdb.ci.CIManager() - ci_dict = m.get_ci_by_id_from_db(ci_id, need_children=False, use_master=False) - - subject = jinja2.Template(notify.get('subject') or "").render(ci_dict) - body = jinja2.Template(notify.get('body') or "").render(ci_dict) - - if notify.get('wx_to'): - to_user = jinja2.Template('|'.join(notify['wx_to'])).render(ci_dict) - url = current_app.config.get("WX_URI") - data = {"to_user": to_user, "content": subject} - try: - requests.post(url, data=data) - except Exception as e: - current_app.logger.error(str(e)) - - if notify.get('mail_to'): - try: - if len(subject) > 700: - subject = subject[:600] + "..." + subject[-100:] - - send_mail("", [_wrap_mail(jinja2.Template(i).render(ci_dict)) - for i in notify['mail_to'] if i], subject, body) - except Exception as e: - current_app.logger.error("Send mail failed: {0}".format(str(e))) - - @celery.task(name="cmdb.calc_computed_attribute", queue=CMDB_QUEUE) def calc_computed_attribute(attr_id, uid): from api.lib.cmdb.ci import CIManager @@ -217,7 +189,8 @@ def calc_computed_attribute(attr_id, uid): current_app.test_request_context().push() login_user(UserCache.get(uid)) + cim = CIManager() for i in CITypeAttribute.get_by(attr_id=attr_id, to_dict=False): cis = CI.get_by(type_id=i.type_id, to_dict=False) for ci in cis: - CIManager.update(ci.id, {}) + cim.update(ci.id, {}) From b8d93bc9eb7ec4c127bacbe93b0d4d627eddc4a3 Mon Sep 17 00:00:00 2001 From: wang-liang0615 <53748875+wang-liang0615@users.noreply.github.com> Date: Tue, 26 Sep 2023 18:25:04 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20UI=E6=9B=B4=E6=96=B0=20=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E5=99=A8=20(#179)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat:新增api&适配 * feat:触发器 * add packages & 注释代码 * feat: webhook tips --- cmdb-ui/package.json | 2 + cmdb-ui/src/api/employee.js | 8 + cmdb-ui/src/modules/cmdb/api/CIType.js | 422 ++++---- cmdb-ui/src/modules/cmdb/api/history.js | 96 +- .../cmdb/components/noticeContent/index.js | 2 + .../cmdb/components/noticeContent/index.vue | 199 ++++ .../cmdb/components/webhook/authorization.vue | 144 +++ .../modules/cmdb/components/webhook/body.vue | 79 ++ .../cmdb/components/webhook/header.vue | 101 ++ .../modules/cmdb/components/webhook/index.js | 2 + .../modules/cmdb/components/webhook/index.vue | 140 +++ .../cmdb/components/webhook/paramaters.vue | 100 ++ .../cmdb/views/ci/modules/CiDetail.vue | 646 ++++++------ .../cmdb/views/ci_types/triggerForm.vue | 774 ++++++++++---- .../cmdb/views/ci_types/triggerTable.vue | 316 +++--- .../cmdb/views/operation_history/index.vue | 81 +- .../operation_history/modules/ciTable.vue | 841 +++++++-------- .../views/operation_history/modules/pager.vue | 232 ++--- .../operation_history/modules/relation.vue | 807 +++++++-------- .../operation_history/modules/searchForm.vue | 382 +++---- .../modules/triggerTable.vue | 117 +++ .../operation_history/modules/typeTable.vue | 955 +++++++++--------- cmdb-ui/src/views/setting/person/index.vue | 2 +- 23 files changed, 3895 insertions(+), 2553 deletions(-) create mode 100644 cmdb-ui/src/modules/cmdb/components/noticeContent/index.js create mode 100644 cmdb-ui/src/modules/cmdb/components/noticeContent/index.vue create mode 100644 cmdb-ui/src/modules/cmdb/components/webhook/authorization.vue create mode 100644 cmdb-ui/src/modules/cmdb/components/webhook/body.vue create mode 100644 cmdb-ui/src/modules/cmdb/components/webhook/header.vue create mode 100644 cmdb-ui/src/modules/cmdb/components/webhook/index.js create mode 100644 cmdb-ui/src/modules/cmdb/components/webhook/index.vue create mode 100644 cmdb-ui/src/modules/cmdb/components/webhook/paramaters.vue create mode 100644 cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue diff --git a/cmdb-ui/package.json b/cmdb-ui/package.json index 1eb3c60..890376a 100644 --- a/cmdb-ui/package.json +++ b/cmdb-ui/package.json @@ -17,6 +17,8 @@ "@babel/plugin-syntax-import-meta": "^7.10.4", "@riophae/vue-treeselect": "^0.4.0", "@vue/composition-api": "^1.7.1", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^1.0.0", "ant-design-vue": "^1.6.5", "axios": "0.18.0", "babel-eslint": "^8.2.2", diff --git a/cmdb-ui/src/api/employee.js b/cmdb-ui/src/api/employee.js index ff695c1..2f2f940 100644 --- a/cmdb-ui/src/api/employee.js +++ b/cmdb-ui/src/api/employee.js @@ -117,3 +117,11 @@ export function getEmployeeListByFilter(data) { data }) } + +export function getNoticeByEmployeeIds(data) { + return axios({ + url: '/common-setting/v1/employee/get_notice_by_ids', + method: 'post', + data + }) +} \ No newline at end of file diff --git a/cmdb-ui/src/modules/cmdb/api/CIType.js b/cmdb-ui/src/modules/cmdb/api/CIType.js index a2c54f2..d7d6631 100644 --- a/cmdb-ui/src/modules/cmdb/api/CIType.js +++ b/cmdb-ui/src/modules/cmdb/api/CIType.js @@ -1,207 +1,215 @@ -import { axios } from '@/utils/request' - -/** - * 获取 所有的 ci_types - * @param parameter - * @returns {AxiosPromise} - */ -export function getCITypes(parameter) { - return axios({ - url: '/v0.1/ci_types', - method: 'GET', - params: parameter - }) -} - -/** - * 获取 某个 ci_types - * @param CITypeName - * @param parameter - * @returns {AxiosPromise} - */ -export function getCIType(CITypeName, parameter) { - return axios({ - url: `/v0.1/ci_types/${CITypeName}`, - method: 'GET', - params: parameter - }) -} - -/** - * 创建 ci_type - * @param data - * @returns {AxiosPromise} - */ -export function createCIType(data) { - return axios({ - url: '/v0.1/ci_types', - method: 'POST', - data: data - }) -} - -/** - * 更新 ci_type - * @param CITypeId - * @param data - * @returns {AxiosPromise} - */ -export function updateCIType(CITypeId, data) { - return axios({ - url: `/v0.1/ci_types/${CITypeId}`, - method: 'PUT', - data: data - }) -} - -/** - * 删除 ci_type - * @param CITypeId - * @returns {AxiosPromise} - */ -export function deleteCIType(CITypeId) { - return axios({ - url: `/v0.1/ci_types/${CITypeId}`, - method: 'DELETE' - }) -} - -/** - * 获取 某个 ci_type 的分组 - * @param CITypeId - * @param data - * @returns {AxiosPromise} - */ -export function getCITypeGroupById(CITypeId, data) { - return axios({ - url: `/v0.1/ci_types/${CITypeId}/attribute_groups`, - method: 'GET', - params: data - }) -} - -/** - * 保存 某个 ci_type 的分组 - * @param CITypeId - * @param data - * @returns {AxiosPromise} - */ -export function createCITypeGroupById(CITypeId, data) { - return axios({ - url: `/v0.1/ci_types/${CITypeId}/attribute_groups`, - method: 'POST', - data: data - }) -} - -/** - * 修改 某个 ci_type 的分组 - * @param groupId - * @param data - * @returns {AxiosPromise} - */ -export function updateCITypeGroupById(groupId, data) { - return axios({ - url: `/v0.1/ci_types/attribute_groups/${groupId}`, - method: 'PUT', - data: data - }) -} - -/** - * 删除 某个 ci_type 的分组 - * @param groupId - * @param data - * @returns {AxiosPromise} - */ -export function deleteCITypeGroupById(groupId, data) { - return axios({ - url: `/v0.1/ci_types/attribute_groups/${groupId}`, - method: 'delete', - data: data - }) -} - -export function getUniqueConstraintList(type_id) { - return axios({ - url: `/v0.1/ci_types/${type_id}/unique_constraint`, - method: 'get', - }) -} - -export function addUniqueConstraint(type_id, data) { - return axios({ - url: `/v0.1/ci_types/${type_id}/unique_constraint`, - method: 'post', - data: data - }) -} - -export function updateUniqueConstraint(type_id, id, data) { - return axios({ - url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`, - method: 'put', - data: data - }) -} - -export function deleteUniqueConstraint(type_id, id) { - return axios({ - url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`, - method: 'delete', - }) -} - -export function getTriggerList(type_id) { - return axios({ - url: `/v0.1/ci_types/${type_id}/triggers`, - method: 'get', - }) -} - -export function addTrigger(type_id, data) { - return axios({ - url: `/v0.1/ci_types/${type_id}/triggers`, - method: 'post', - data: data - }) -} - -export function updateTrigger(type_id, id, data) { - return axios({ - url: `/v0.1/ci_types/${type_id}/triggers/${id}`, - method: 'put', - data: data - }) -} - -export function deleteTrigger(type_id, id) { - return axios({ - url: `/v0.1/ci_types/${type_id}/triggers/${id}`, - method: 'delete', - }) -} - -// CMDB的模型和实例的授权接口 -export function grantCiType(type_id, rid, data) { - return axios({ - url: `/v0.1/ci_types/${type_id}/roles/${rid}/grant`, - method: 'post', - data - }) -} -// CMDB的模型和实例的删除授权接口 -export function revokeCiType(type_id, rid, data) { - return axios({ - url: `/v0.1/ci_types/${type_id}/roles/${rid}/revoke`, - method: 'post', - data - }) -} -// CMDB的模型和实例的过滤的权限 -export function ciTypeFilterPermissions(type_id) { - return axios({ - url: `/v0.1/ci_types/${type_id}/filters/permissions`, - method: 'get', - }) -} +import { axios } from '@/utils/request' + +/** + * 获取 所有的 ci_types + * @param parameter + * @returns {AxiosPromise} + */ +export function getCITypes(parameter) { + return axios({ + url: '/v0.1/ci_types', + method: 'GET', + params: parameter + }) +} + +/** + * 获取 某个 ci_types + * @param CITypeName + * @param parameter + * @returns {AxiosPromise} + */ +export function getCIType(CITypeName, parameter) { + return axios({ + url: `/v0.1/ci_types/${CITypeName}`, + method: 'GET', + params: parameter + }) +} + +/** + * 创建 ci_type + * @param data + * @returns {AxiosPromise} + */ +export function createCIType(data) { + return axios({ + url: '/v0.1/ci_types', + method: 'POST', + data: data + }) +} + +/** + * 更新 ci_type + * @param CITypeId + * @param data + * @returns {AxiosPromise} + */ +export function updateCIType(CITypeId, data) { + return axios({ + url: `/v0.1/ci_types/${CITypeId}`, + method: 'PUT', + data: data + }) +} + +/** + * 删除 ci_type + * @param CITypeId + * @returns {AxiosPromise} + */ +export function deleteCIType(CITypeId) { + return axios({ + url: `/v0.1/ci_types/${CITypeId}`, + method: 'DELETE' + }) +} + +/** + * 获取 某个 ci_type 的分组 + * @param CITypeId + * @param data + * @returns {AxiosPromise} + */ +export function getCITypeGroupById(CITypeId, data) { + return axios({ + url: `/v0.1/ci_types/${CITypeId}/attribute_groups`, + method: 'GET', + params: data + }) +} + +/** + * 保存 某个 ci_type 的分组 + * @param CITypeId + * @param data + * @returns {AxiosPromise} + */ +export function createCITypeGroupById(CITypeId, data) { + return axios({ + url: `/v0.1/ci_types/${CITypeId}/attribute_groups`, + method: 'POST', + data: data + }) +} + +/** + * 修改 某个 ci_type 的分组 + * @param groupId + * @param data + * @returns {AxiosPromise} + */ +export function updateCITypeGroupById(groupId, data) { + return axios({ + url: `/v0.1/ci_types/attribute_groups/${groupId}`, + method: 'PUT', + data: data + }) +} + +/** + * 删除 某个 ci_type 的分组 + * @param groupId + * @param data + * @returns {AxiosPromise} + */ +export function deleteCITypeGroupById(groupId, data) { + return axios({ + url: `/v0.1/ci_types/attribute_groups/${groupId}`, + method: 'delete', + data: data + }) +} + +export function getUniqueConstraintList(type_id) { + return axios({ + url: `/v0.1/ci_types/${type_id}/unique_constraint`, + method: 'get', + }) +} + +export function addUniqueConstraint(type_id, data) { + return axios({ + url: `/v0.1/ci_types/${type_id}/unique_constraint`, + method: 'post', + data: data + }) +} + +export function updateUniqueConstraint(type_id, id, data) { + return axios({ + url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`, + method: 'put', + data: data + }) +} + +export function deleteUniqueConstraint(type_id, id) { + return axios({ + url: `/v0.1/ci_types/${type_id}/unique_constraint/${id}`, + method: 'delete', + }) +} + +export function getTriggerList(type_id) { + return axios({ + url: `/v0.1/ci_types/${type_id}/triggers`, + method: 'get', + }) +} + +export function addTrigger(type_id, data) { + return axios({ + url: `/v0.1/ci_types/${type_id}/triggers`, + method: 'post', + data: data + }) +} + +export function updateTrigger(type_id, id, data) { + return axios({ + url: `/v0.1/ci_types/${type_id}/triggers/${id}`, + method: 'put', + data: data + }) +} + +export function deleteTrigger(type_id, id) { + return axios({ + url: `/v0.1/ci_types/${type_id}/triggers/${id}`, + method: 'delete', + }) +} + +// CMDB的模型和实例的授权接口 +export function grantCiType(type_id, rid, data) { + return axios({ + url: `/v0.1/ci_types/${type_id}/roles/${rid}/grant`, + method: 'post', + data + }) +} +// CMDB的模型和实例的删除授权接口 +export function revokeCiType(type_id, rid, data) { + return axios({ + url: `/v0.1/ci_types/${type_id}/roles/${rid}/revoke`, + method: 'post', + data + }) +} +// CMDB的模型和实例的过滤的权限 +export function ciTypeFilterPermissions(type_id) { + return axios({ + url: `/v0.1/ci_types/${type_id}/filters/permissions`, + method: 'get', + }) +} + +export function getAllDagsName(params) { + return axios({ + url: '/v1/dag/all_names', + method: 'GET', + params: params + }) +} diff --git a/cmdb-ui/src/modules/cmdb/api/history.js b/cmdb-ui/src/modules/cmdb/api/history.js index 8566fa6..ae8a099 100644 --- a/cmdb-ui/src/modules/cmdb/api/history.js +++ b/cmdb-ui/src/modules/cmdb/api/history.js @@ -1,40 +1,56 @@ -import { axios } from '@/utils/request' - -export function getCIHistory (ciId) { - return axios({ - url: `/v0.1/history/ci/${ciId}`, - method: 'GET' - }) -} - -export function getCIHistoryTable (params) { - return axios({ - url: `/v0.1/history/records/attribute`, - method: 'GET', - params: params - }) -} - -export function getRelationTable (params) { - return axios({ - url: `/v0.1/history/records/relation`, - method: 'GET', - params: params - }) -} - -export function getCITypesTable (params) { - return axios({ - url: `/v0.1/history/ci_types`, - method: 'GET', - params: params - }) -} - -export function getUsers (params) { - return axios({ - url: `/v1/acl/users/employee`, - method: 'GET', - params: params - }) -} +import { axios } from '@/utils/request' + +export function getCIHistory(ciId) { + return axios({ + url: `/v0.1/history/ci/${ciId}`, + method: 'GET' + }) +} + +export function getCIHistoryTable(params) { + return axios({ + url: `/v0.1/history/records/attribute`, + method: 'GET', + params: params + }) +} + +export function getRelationTable(params) { + return axios({ + url: `/v0.1/history/records/relation`, + method: 'GET', + params: params + }) +} + +export function getCITypesTable(params) { + return axios({ + url: `/v0.1/history/ci_types`, + method: 'GET', + params: params + }) +} + +export function getUsers(params) { + return axios({ + url: `/v1/acl/users/employee`, + method: 'GET', + params: params + }) +} + +export function getCiTriggers(params) { + return axios({ + url: `/v0.1/history/ci_triggers`, + method: 'GET', + params: params + }) +} + +export function getCiTriggersByCiId(ci_id, params) { + return axios({ + url: `/v0.1/history/ci_triggers/${ci_id}`, + method: 'GET', + params + }) +} diff --git a/cmdb-ui/src/modules/cmdb/components/noticeContent/index.js b/cmdb-ui/src/modules/cmdb/components/noticeContent/index.js new file mode 100644 index 0000000..926d0db --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/noticeContent/index.js @@ -0,0 +1,2 @@ +import NoticeContent from './index.vue' +export default NoticeContent diff --git a/cmdb-ui/src/modules/cmdb/components/noticeContent/index.vue b/cmdb-ui/src/modules/cmdb/components/noticeContent/index.vue new file mode 100644 index 0000000..977ff31 --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/noticeContent/index.vue @@ -0,0 +1,199 @@ + + + + + + + diff --git a/cmdb-ui/src/modules/cmdb/components/webhook/authorization.vue b/cmdb-ui/src/modules/cmdb/components/webhook/authorization.vue new file mode 100644 index 0000000..3afb659 --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/webhook/authorization.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/cmdb-ui/src/modules/cmdb/components/webhook/body.vue b/cmdb-ui/src/modules/cmdb/components/webhook/body.vue new file mode 100644 index 0000000..a30b1ce --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/webhook/body.vue @@ -0,0 +1,79 @@ + + + + + + diff --git a/cmdb-ui/src/modules/cmdb/components/webhook/header.vue b/cmdb-ui/src/modules/cmdb/components/webhook/header.vue new file mode 100644 index 0000000..d039016 --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/webhook/header.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/cmdb-ui/src/modules/cmdb/components/webhook/index.js b/cmdb-ui/src/modules/cmdb/components/webhook/index.js new file mode 100644 index 0000000..dea91c2 --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/webhook/index.js @@ -0,0 +1,2 @@ +import Webhook from './index.vue' +export default Webhook diff --git a/cmdb-ui/src/modules/cmdb/components/webhook/index.vue b/cmdb-ui/src/modules/cmdb/components/webhook/index.vue new file mode 100644 index 0000000..e1d625c --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/webhook/index.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/cmdb-ui/src/modules/cmdb/components/webhook/paramaters.vue b/cmdb-ui/src/modules/cmdb/components/webhook/paramaters.vue new file mode 100644 index 0000000..93238e3 --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/components/webhook/paramaters.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue index 303cbb4..006d3b0 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue @@ -1,319 +1,327 @@ - - - - - - + + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue index 5c84d13..13ea097 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue @@ -1,197 +1,577 @@ - - - - - + + + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerTable.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerTable.vue index 0bbc0a1..c67ed1c 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerTable.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerTable.vue @@ -1,144 +1,172 @@ - - - - - + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/index.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/index.vue index 6b6ed63..d81a97c 100644 --- a/cmdb-ui/src/modules/cmdb/views/operation_history/index.vue +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/index.vue @@ -1,38 +1,43 @@ - - - - - + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/ciTable.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/ciTable.vue index 5745baf..76e2fed 100644 --- a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/ciTable.vue +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/ciTable.vue @@ -1,420 +1,421 @@ - - - - + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/pager.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/pager.vue index 0b7352a..266c9db 100644 --- a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/pager.vue +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/pager.vue @@ -1,116 +1,116 @@ - - - - - + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/relation.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/relation.vue index bb8eadb..d177ca1 100644 --- a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/relation.vue +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/relation.vue @@ -1,403 +1,404 @@ - - - - - + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/searchForm.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/searchForm.vue index a76820b..2491035 100644 --- a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/searchForm.vue +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/searchForm.vue @@ -1,191 +1,191 @@ - - - - - + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue new file mode 100644 index 0000000..bddc78d --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/typeTable.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/typeTable.vue index 6bc8e13..d743c2d 100644 --- a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/typeTable.vue +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/typeTable.vue @@ -1,477 +1,478 @@ - - - - - + + + + + diff --git a/cmdb-ui/src/views/setting/person/index.vue b/cmdb-ui/src/views/setting/person/index.vue index e99feea..6c79c63 100644 --- a/cmdb-ui/src/views/setting/person/index.vue +++ b/cmdb-ui/src/views/setting/person/index.vue @@ -94,7 +94,7 @@
- {{ form.wx_id ? '重新绑定' : '绑定' }} + {{ form.notice_info.wechatApp ? '重新绑定' : '绑定' }}
From 8e6ce05c041d064506b92b4748390444afce776c Mon Sep 17 00:00:00 2001 From: simontigers <47096077+simontigers@users.noreply.github.com> Date: Tue, 26 Sep 2023 19:44:20 +0800 Subject: [PATCH 3/3] feat: common notice config (#180) --- cmdb-api/api/commands/click_common_setting.py | 56 +++++++++++ .../api/lib/common_setting/notice_config.py | 94 +++++++++++++++++++ .../api/lib/common_setting/resp_format.py | 1 + cmdb-api/api/models/common_setting.py | 9 ++ .../api/views/common_setting/notice_config.py | 71 ++++++++++++++ 5 files changed, 231 insertions(+) create mode 100644 cmdb-api/api/lib/common_setting/notice_config.py create mode 100644 cmdb-api/api/views/common_setting/notice_config.py diff --git a/cmdb-api/api/commands/click_common_setting.py b/cmdb-api/api/commands/click_common_setting.py index e0fea63..f886e9b 100644 --- a/cmdb-api/api/commands/click_common_setting.py +++ b/cmdb-api/api/commands/click_common_setting.py @@ -230,3 +230,59 @@ def init_department(): cli.init_wide_company() cli.create_acl_role_with_department() cli.init_backend_resource() + + +@click.command() +@with_appcontext +def common_check_new_columns(): + """ + add new columns to tables + """ + from api.extensions import db + from sqlalchemy import inspect, text + + def get_model_by_table_name(table_name): + for model in db.Model.registry._class_registry.values(): + if hasattr(model, '__tablename__') and model.__tablename__ == table_name: + return model + return None + + def add_new_column(table_name, new_column): + column_type = new_column.type.compile(engine.dialect) + default_value = new_column.default.arg if new_column.default else None + + sql = f"ALTER TABLE {table_name} ADD COLUMN {new_column.name} {column_type} " + if new_column.comment: + sql += f" comment '{new_column.comment}'" + + if column_type == 'JSON': + pass + elif default_value: + if column_type.startswith('VAR') or column_type.startswith('Text'): + if default_value is None or len(default_value) == 0: + pass + else: + sql += f" DEFAULT {default_value}" + + sql = text(sql) + db.session.execute(sql) + + engine = db.get_engine() + inspector = inspect(engine) + table_names = inspector.get_table_names() + for table_name in table_names: + existed_columns = inspector.get_columns(table_name) + existed_column_name_list = [c['name'] for c in existed_columns] + + model = get_model_by_table_name(table_name) + if model is None: + continue + model_columns = model.__table__.columns._all_columns + for column in model_columns: + if column.name not in existed_column_name_list: + try: + add_new_column(table_name, column) + current_app.logger.info(f"add new column [{column.name}] in table [{table_name}] success.") + except Exception as e: + current_app.logger.error(f"add new column [{column.name}] in table [{table_name}] err:") + current_app.logger.error(e) diff --git a/cmdb-api/api/lib/common_setting/notice_config.py b/cmdb-api/api/lib/common_setting/notice_config.py new file mode 100644 index 0000000..af8ff87 --- /dev/null +++ b/cmdb-api/api/lib/common_setting/notice_config.py @@ -0,0 +1,94 @@ +from api.models.common_setting import NoticeConfig +from wtforms import Form +from wtforms import StringField +from wtforms import validators +from flask import abort +import smtplib +from email.mime.text import MIMEText +from email.utils import formataddr + + +class NoticeConfigCRUD(object): + + @staticmethod + def add_notice_config(**kwargs): + NoticeConfigCRUD.check_platform(kwargs.get('platform')) + try: + return NoticeConfig.create( + **kwargs + ) + + except Exception as e: + return abort(400, str(e)) + + @staticmethod + def check_platform(platform): + NoticeConfig.get_by(first=True, to_dict=False, platform=platform) and abort(400, f"{platform} 已存在!") + + @staticmethod + def edit_notice_config(_id, **kwargs): + existed = NoticeConfigCRUD.get_notice_config_by_id(_id) + try: + return existed.update(**kwargs) + except Exception as e: + return abort(400, str(e)) + + @staticmethod + def get_notice_config_by_id(_id): + return NoticeConfig.get_by(first=True, to_dict=False, id=_id) or abort(400, f"{_id} 配置项不存在!") + + @staticmethod + def get_all(): + return NoticeConfig.get_by(to_dict=True) + + @staticmethod + def test_send_email(receive_address, **kwargs): + # 设置发送方和接收方的电子邮件地址 + sender_email = 'test@test.com' + sender_name = 'Test Sender' + recipient_email = receive_address + recipient_name = receive_address + + subject = 'Test Email' + body = 'This is a test email' + + message = MIMEText(body, 'plain', 'utf-8') + message['From'] = formataddr((sender_name, sender_email)) + message['To'] = formataddr((recipient_name, recipient_email)) + message['Subject'] = subject + + smtp_server = kwargs.get('server') + smtp_port = kwargs.get('port') + smtp_username = kwargs.get('username') + smtp_password = kwargs.get('password') + + if kwargs.get('mail_type') == 'SMTP': + smtp_connection = smtplib.SMTP(smtp_server, smtp_port) + else: + smtp_connection = smtplib.SMTP_SSL(smtp_server, smtp_port) + + if kwargs.get('is_login'): + smtp_connection.login(smtp_username, smtp_password) + + smtp_connection.sendmail(sender_email, recipient_email, message.as_string()) + smtp_connection.quit() + + return 1 + + +class NoticeConfigForm(Form): + platform = StringField(validators=[ + validators.DataRequired(message="平台 不能为空"), + validators.Length(max=255), + ]) + info = StringField(validators=[ + validators.DataRequired(message="信息 不能为空"), + validators.Length(max=255), + ]) + + +class NoticeConfigUpdateForm(Form): + info = StringField(validators=[ + validators.DataRequired(message="信息 不能为空"), + validators.Length(max=255), + ]) diff --git a/cmdb-api/api/lib/common_setting/resp_format.py b/cmdb-api/api/lib/common_setting/resp_format.py index a50c938..a9c6a35 100644 --- a/cmdb-api/api/lib/common_setting/resp_format.py +++ b/cmdb-api/api/lib/common_setting/resp_format.py @@ -53,5 +53,6 @@ class ErrFormat(CommonErrFormat): username_is_required = "username不能为空" email_is_required = "邮箱不能为空" email_format_error = "邮箱格式错误" + email_send_timeout = "邮件发送超时" common_data_not_found = "ID {} 找不到记录" diff --git a/cmdb-api/api/models/common_setting.py b/cmdb-api/api/models/common_setting.py index f222ea9..a141ee8 100644 --- a/cmdb-api/api/models/common_setting.py +++ b/cmdb-api/api/models/common_setting.py @@ -47,6 +47,8 @@ class Employee(ModelWithoutPK): last_login = db.Column(db.TIMESTAMP, nullable=True) block = db.Column(db.Integer, default=0) + notice_info = db.Column(db.JSON, default={}) + _department = db.relationship( 'Department', backref='common_employee.department_id', lazy='joined' @@ -87,3 +89,10 @@ class CommonData(Model): data_type = db.Column(db.VARCHAR(255), default='') data = db.Column(db.JSON) + + +class NoticeConfig(Model): + __tablename__ = "common_notice_config" + + platform = db.Column(db.VARCHAR(255), nullable=False) + info = db.Column(db.JSON) diff --git a/cmdb-api/api/views/common_setting/notice_config.py b/cmdb-api/api/views/common_setting/notice_config.py new file mode 100644 index 0000000..6f09196 --- /dev/null +++ b/cmdb-api/api/views/common_setting/notice_config.py @@ -0,0 +1,71 @@ +from flask import request, abort, current_app +from werkzeug.datastructures import MultiDict + +from api.lib.perm.auth import auth_with_app_token +from api.models.common_setting import NoticeConfig +from api.resource import APIView +from api.lib.common_setting.notice_config import NoticeConfigForm, NoticeConfigUpdateForm, NoticeConfigCRUD +from api.lib.decorator import args_required +from api.lib.common_setting.resp_format import ErrFormat + +prefix = '/notice_config' + + +class NoticeConfigView(APIView): + url_prefix = (f'{prefix}',) + + @args_required('platform') + @auth_with_app_token + def get(self): + platform = request.args.get('platform') + res = NoticeConfig.get_by(first=True, to_dict=True, platform=platform) or {} + return self.jsonify(res) + + def post(self): + form = NoticeConfigForm(MultiDict(request.json)) + if not form.validate(): + abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg)) for filed, msg in form.errors.items()])) + + data = NoticeConfigCRUD.add_notice_config(**form.data) + return self.jsonify(data.to_dict()) + + +class NoticeConfigUpdateView(APIView): + url_prefix = (f'{prefix}/',) + + def put(self, _id): + form = NoticeConfigUpdateForm(MultiDict(request.json)) + if not form.validate(): + abort(400, ','.join(['{}: {}'.format(filed, ','.join(msg)) for filed, msg in form.errors.items()])) + + data = NoticeConfigCRUD.edit_notice_config(_id, **form.data) + return self.jsonify(data.to_dict()) + + +class CheckEmailServer(APIView): + url_prefix = (f'{prefix}/send_test_email',) + + def post(self): + receive_address = request.args.get('receive_address') + info = request.values.get('info') + + try: + + result = NoticeConfigCRUD.test_send_email(receive_address, **info) + return self.jsonify(result=result) + except Exception as e: + current_app.logger.error('test_send_email err:') + current_app.logger.error(e) + if 'Timed Out' in str(e): + abort(400, ErrFormat.email_send_timeout) + abort(400, f"{str(e)}") + + +class NoticeConfigGetView(APIView): + method_decorators = [] + url_prefix = (f'{prefix}/all',) + + @auth_with_app_token + def get(self): + res = NoticeConfigCRUD.get_all() + return self.jsonify(res)