diff --git a/src/components/advancedSearch/TagMode.vue b/src/components/advancedSearch/TagMode.vue index 4651c546..44a8d015 100644 --- a/src/components/advancedSearch/TagMode.vue +++ b/src/components/advancedSearch/TagMode.vue @@ -54,22 +54,22 @@ @@ -79,7 +79,6 @@ @@ -88,6 +87,9 @@ +
+ +
@@ -99,8 +101,8 @@ import Meta, { connection, condition, columnType } from './meta/meta' import _ from 'lodash' import { handleErrorTip } from '@/components/advancedSearch/meta/error' -import Parser, { stringInQuot } from '@/components/advancedSearch/meta/parser' -import { comparedEntityKey, overwriteUrl, urlParamsHandler } from '@/utils/tools' +import Parser, { handleMetaListToStr, stringInQuot } from '@/components/advancedSearch/meta/parser' +import { comparedEntityKey, handleEntityTypeByStr, overwriteUrl, urlParamsHandler } from '@/utils/tools' export default { name: 'TagMode', props: { @@ -329,7 +331,9 @@ export default { const parser = new Parser(this.columnList) const errorList = parser.validateMeta(this.metaList) if (_.isEmpty(errorList)) { - this.$emit('search', parser.parseMeta(this.metaList)) + const str = handleMetaListToStr(this.metaList) + const keyInfo = comparedEntityKey(handleEntityTypeByStr(str)) + this.$emit('search', { ...parser.parseStr(keyInfo.key), str: str }) } else { this.$message.error(handleErrorTip(errorList[0])) } @@ -431,6 +435,9 @@ export default { newUrl = urlParamsHandler(window.location.href, query, newParam, clean) } overwriteUrl(newUrl) + }, + cleanMetaList () { + this.metaList = [] } }, mounted () { diff --git a/src/components/advancedSearch/TextMode.vue b/src/components/advancedSearch/TextMode.vue index 16ba1cd9..bb83ebf1 100644 --- a/src/components/advancedSearch/TextMode.vue +++ b/src/components/advancedSearch/TextMode.vue @@ -36,7 +36,7 @@ import { toRaw } from 'vue' import _ from 'lodash' import { columnType } from '@/components/advancedSearch/meta/meta' import { handleErrorTip } from '@/components/advancedSearch/meta/error' -import { comparedEntityKey, overwriteUrl, urlParamsHandler } from '@/utils/tools' +import { comparedEntityKey, handleEntityTypeByStr, overwriteUrl, urlParamsHandler } from '@/utils/tools' export default { name: 'TextMode', @@ -57,6 +57,8 @@ export default { methods: { cleanParams () { toRaw(this.codeMirror).setValue('') + this.isEdit = false + this.isCloseIcon = false const routeQuery = this.$route.query delete routeQuery.q this.reloadUrl(routeQuery, 'cleanOldParams') @@ -72,9 +74,8 @@ export default { this.codeMirror.setOption('extraKeys', { Enter: (cm) => {} }) - const str = this.codeMirror.getValue().trim() this.codeMirror.on('focus', () => { - if (str !== '') { + if (this.codeMirror.getValue().trim() !== '') { this.isEdit = true this.isCloseIcon = true } @@ -95,7 +96,7 @@ export default { const str = this.codeMirror.getValue().trim() if (str) { const parser = new Parser(this.columnList) - const keyInfo = comparedEntityKey(str) + const keyInfo = comparedEntityKey(handleEntityTypeByStr(str)) if (keyInfo.isKey) { const errorList = parser.validateStr(keyInfo.key) if (_.isEmpty(errorList)) { @@ -104,7 +105,7 @@ export default { this.$message.error(handleErrorTip(errorList[0])) } } else { - this.$message.error(this.$t('tip.invalidQueryField') + keyInfo.key) + this.$message.error(this.$t('tip.invalidQueryField') + ' ' + keyInfo.key) } } else { this.$emit('search', { q: '', str: '', metaList: [] }) diff --git a/src/components/advancedSearch/meta/meta.js b/src/components/advancedSearch/meta/meta.js index 1444900e..9ffb9a4b 100644 --- a/src/components/advancedSearch/meta/meta.js +++ b/src/components/advancedSearch/meta/meta.js @@ -13,11 +13,11 @@ export const defaultConnectionList = [ { value: 'AND', label: 'AND' - }, - { - value: 'OR', - label: 'OR' } + // { + // value: 'OR', + // label: 'OR' + // } ] export default class Meta { // meta元数据有两种,一是condition,表示字段、操作符、值的组合,二是connection,是condition之间的连接符,AND | OR diff --git a/src/components/advancedSearch/meta/parser.js b/src/components/advancedSearch/meta/parser.js index 96fe2ec0..ccbfa0d7 100644 --- a/src/components/advancedSearch/meta/parser.js +++ b/src/components/advancedSearch/meta/parser.js @@ -348,8 +348,10 @@ export default class Parser { } } } - } else if (['o', 'a'].indexOf(s.toLowerCase()) > -1) { + // } else if (['o', 'a'].indexOf(s.toLowerCase()) > -1) { + } else if (['a'].indexOf(s.toLowerCase()) > -1) { // 前一位是否是空格,否则视为普通字符串 + // 处理连接符为or的情况 if (s.toLowerCase() === 'o') { if (strArr[i + 1] && strArr[i + 1].toLowerCase() === 'r' && strArr[i + 2] && strArr[i + 2] === ' ') { token = new Token(types.connection, 'OR') @@ -763,3 +765,65 @@ export function stringInQuot (value) { export function handleOperatorSpace (operator) { return ['IN', 'NOT IN', 'LIKE', 'NOT LIKE'].indexOf(operator) > -1 ? ` ${operator} ` : operator } + +/** + * 将metaList转为字符串 + * @param metaList + * @returns {string|*} + */ +export function handleMetaListToStr (metaList) { + // 长度为1时,即模糊搜索,例如搜索框值为1.1.1.1,则直接返回1.1.1.1 + if (metaList && metaList.length === 1) { + return metaList[0].column.label + } else if (metaList && metaList.length > 1) { + // 此为按语法搜索,将metaList转为字符串 + const newMetaList = [] + // 去除metaList的AND项 + metaList.forEach(item => { + if (item.value !== 'AND') { + newMetaList.push(item) + } + }) + + const newObj = combineLabel(newMetaList) + const lastObj = {} + for (const i in newObj) { + if (newObj[i].indexOf('(') > -1) { + // 原来为IN ()格式的直接保留 + lastObj[i] = `${i} IN ${newObj[i]}` + } else if (newObj[i].indexOf(',') > -1) { + // 格式为IP: '1.1.1.1,2.2.2.2'的,改成IP: "'1.1.1.1','2.2.2.2'",然后再()括号包裹 + let str = newObj[i] + str = str.replace(/(\d+\.\d+\.\d+\.\d+),(\d+\.\d+\.\d+\.\d+)/g, "'$1','$2'") + lastObj[i] = `${i} IN (${str})` + } else { + // 单独存在的,直接保留 + lastObj[i] = `${i} = ${newObj[i]}` + } + } + + let str = '' + for (const i in lastObj) { + str += lastObj[i] + ' AND ' + } + str = str.slice(0, -5) + + return str + } +} + +/** + * 将相同属性的label组合到一起,即IP='1.1.1.1' AND IP='2.2.2.2'组合成IP: '1.1.1.1,2.2.2.2' + * @param list + * @returns {*} + */ +const combineLabel = (list) => { + return list.reduce((acc, cur) => { + if (acc[cur.column.label]) { + acc[cur.column.label] += `,${cur.value.label}` + } else { + acc[cur.column.label] = cur.value.label + } + return acc + }, {}) +} diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 7b984a86..96eebee9 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -116,6 +116,11 @@ const user = { localStorage.setItem(storageKey.linkInfo, res.page.list[0].cvalue) } }) + get(api.config, { ckey: 'schema_entity_explore' }).then(res => { + if (res.code === 200 && res.page.list && res.page.list.length > 0) { + localStorage.setItem(storageKey.schemaEntityExplore, res.page.list[0].cvalue) + } + }) }, logoutSuccess (store, res) { localStorage.removeItem(storageKey.username) diff --git a/src/utils/constants.js b/src/utils/constants.js index 5f9e0caa..df6db66e 100644 --- a/src/utils/constants.js +++ b/src/utils/constants.js @@ -43,7 +43,8 @@ export const storageKey = { drillDownTableConfig: 'drilldownTableConfig', userCustomizationConfig: 'userCustomizationConfig', linkInfo: 'cn-link-info', - history: 'cn-history' + history: 'cn-history', + schemaEntityExplore: 'schema_entity_explore' } export const largeCountryList = ['CN', 'US', 'RU', 'AU', 'CA', 'KZ', 'IN', 'BR'] diff --git a/src/utils/static-data.js b/src/utils/static-data.js index ab92b1b9..ea8e88ab 100644 --- a/src/utils/static-data.js +++ b/src/utils/static-data.js @@ -7,7 +7,7 @@ */ // 如有使用this的 import i18n from '@/i18n' -import { unitTypes } from '@/utils/constants' +import { storageKey, unitTypes } from '@/utils/constants' const _this = i18n.global _this.$t = _this.t @@ -270,7 +270,7 @@ export const dataForNpmNetworkQuantity = { { name: 'overall.packetRetrans' } ] } -export const columnList = [ +export const columnList1 = [ { name: 'ip', type: 'string', @@ -334,14 +334,20 @@ export const columnList = [ } } ] + +let schemaEntityExplore = localStorage.getItem(storageKey.schemaEntityExplore) +// todo enityMetadata字段后续可能会改 +schemaEntityExplore = schemaEntityExplore ? JSON.parse(schemaEntityExplore).enityMetadata : columnList1 +export const columnList = schemaEntityExplore + export const operatorList = ['=', '!=', /* '>', '<', '>=', '<=', */'IN', 'NOT IN', 'LIKE', 'NOT LIKE'] export const connectionList = [ { value: 'AND', label: 'AND' - }, - { - value: 'OR', - label: 'OR' } + // { + // value: 'OR', + // label: 'OR' + // } ] diff --git a/src/utils/tools.js b/src/utils/tools.js index 4611446f..73f38ae7 100644 --- a/src/utils/tools.js +++ b/src/utils/tools.js @@ -8,7 +8,7 @@ import router from '@/router' import indexedDBUtils from '@/indexedDB' // columnList从'@/utils/static-data'引入的话,会导致国际化i18n出错 -const columnList = [ +const columnList1 = [ { name: 'ip', type: 'string', @@ -72,6 +72,10 @@ const columnList = [ } } ] +let schemaEntityExplore = localStorage.getItem(storageKey.schemaEntityExplore) +// todo enityMetadata字段后续可能会改 +schemaEntityExplore = schemaEntityExplore ? JSON.parse(schemaEntityExplore).enityMetadata : columnList1 +export const columnList = schemaEntityExplore export const tableSort = { // 是否需要排序 @@ -1387,6 +1391,10 @@ export function switchStatus (status) { */ export function comparedEntityKey (q) { if (q && q.indexOf('=') > -1) { + // =周围有空格,则去除空格 + const regex = /\ = | =|= /g + q = q.replace(regex, '=') + if (q.indexOf('AND') > -1) { const arr = q.split(' AND ') let newQ = '' @@ -1414,6 +1422,11 @@ export function comparedEntityKey (q) { } return returnObj + } else if (q.indexOf('LIKE') > -1) { + return { + key: q, + isKey: true + } } else { const key = q.substring(0, q.indexOf('=')) const obj = columnList.find(t => t.label.toLowerCase() === key.toLowerCase()) @@ -1423,6 +1436,11 @@ export function comparedEntityKey (q) { return { key: '[' + key + ']', isKey: false } } } + } else if (q && q.indexOf(' IN ') > -1) { + return { + key: q, + isKey: true + } } else { return { key: q, @@ -1430,3 +1448,48 @@ export function comparedEntityKey (q) { } } } + +/** + * 将模糊查询传过来的str转换为对应的实体类型,不满足ip和domain格式的当成app + * @param str + * @returns {string} + */ +export const handleEntityTypeByStr = (str) => { + if (str) { + const regex = /^["']|["']$/ + // 去除两侧引号,如'1.1.1.1',避免校验时被当作app + console.log('模糊查询', str, regex.test(str)) + if (regex.test(str)) { + str = str.replace(/^['"]+|['"]+$/g, '') + } + console.log('模糊查询222', str, str.trim()) + + const arr = ['IP'.toLowerCase(), 'Domain'.toLowerCase(), 'App'.toLowerCase(), 'City'.toLowerCase(), 'Country'.toLowerCase(), 'ASN'.toLowerCase()] + let newStr = JSON.parse(JSON.stringify(str.toLowerCase())) + // 将str中的IP、Domain等替换为数组arr中的元素 + for (let i = 0; i < arr.length; i++) { + newStr = newStr.replace(new RegExp(arr[i], 'g'), arr[i]) + } + // 检查str字段在arr中是否出现 + const result = arr.some(item => newStr.includes(item)) + if (result) { + return str + } + + // ipv4校验 + const regexIPv4 = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/ + // ipv6校验 + const regexIPv6 = /^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%\w+|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])|(?:[0-9A-Fa-f]{1,4}:){1,4}:192\.88\.99\.(\d{1,3})|(?:[0-9A-Fa-f]{1,4}:){1,4}:192\.0\.2\.(\d{1,3})|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:[0-9A-Fa-f]{1,4}:){0,1}192\.0\.0\.(\d{1,3})|ff00:(?::[0-9A-Fa-f]{0,4}){0,4}|(?:[0-9A-Fa-f]{1,4}:){1,4}:255\.255\.255\.255)$/ + // domain校验 + const reg = /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/ + + console.log('ip校验', regexIPv4.test(str), regexIPv6.test(str)) + if (regexIPv4.test(str) || regexIPv6.test(str)) { + return `IP='${str}'` + } else if (reg.test(str)) { + return `Domain LIKE '%${str}'` + } else { + return `App LIKE '%${str}'%` + } + } +} diff --git a/src/views/charts2/Panel.vue b/src/views/charts2/Panel.vue index 4e257dc1..c2c87605 100644 --- a/src/views/charts2/Panel.vue +++ b/src/views/charts2/Panel.vue @@ -254,9 +254,9 @@ export default { // networkOverviewBeforeTab是dashboard点击下钻标识,dimensionType是详情页到下钻页标识 const tab = query.networkOverviewBeforeTab || query.dimensionType const value = query.fourthMenu - const ipList = ['ip', 'clientIp', 'serverIp', 'dnsServer', 'a', 'aaaa'] + const ipList = ['ip', 'clientIp', 'serverIp', 'a', 'aaaa'] const appList = ['appLabel'] - const domainList = ['sslSni'] // domain暂定,先加入snis + const domainList = ['sslSni', 'domain'] if (ipList.indexOf(tab) > -1) { entityType.value = 'ip' diff --git a/src/views/entityExplorer/EntityExplorer.vue b/src/views/entityExplorer/EntityExplorer.vue index 34dc27d1..be4010e2 100644 --- a/src/views/entityExplorer/EntityExplorer.vue +++ b/src/views/entityExplorer/EntityExplorer.vue @@ -168,7 +168,13 @@ import { getNowTime, getSecond } from '@/utils/date-util' import { ref } from 'vue' import _ from 'lodash' import Loading from '@/components/common/Loading' -import { overwriteUrl, urlParamsHandler, numberWithCommas, comparedEntityKey } from '@/utils/tools' +import { + overwriteUrl, + urlParamsHandler, + numberWithCommas, + comparedEntityKey, + handleEntityTypeByStr +} from '@/utils/tools' import Parser from '@/components/advancedSearch/meta/parser' import { handleErrorTip } from '@/components/advancedSearch/meta/error' import { columnList } from '@/utils/static-data' @@ -863,7 +869,7 @@ export default { } } const parser = new Parser(columnList) - const keyInfo = comparedEntityKey(str) + const keyInfo = comparedEntityKey(handleEntityTypeByStr(str)) if (keyInfo.isKey) { const errorList = parser.validateStr(keyInfo.key) if (_.isEmpty(errorList)) { @@ -872,7 +878,7 @@ export default { this.$message.error(handleErrorTip(errorList[0])) } } else { - this.$message.error(this.$t('tip.invalidQueryField') + keyInfo.key) + this.$message.error(this.$t('tip.invalidQueryField') + ' ' + keyInfo.key) } } else { this.search({ q: '', str: '', metaList: [] }) diff --git a/src/views/entityExplorer/entityList/Row.vue b/src/views/entityExplorer/entityList/Row.vue index d768a0e5..99a4fc76 100644 --- a/src/views/entityExplorer/entityList/Row.vue +++ b/src/views/entityExplorer/entityList/Row.vue @@ -30,7 +30,7 @@
- {{ $t('overall.region') }} :   + {{ $t('overall.city') }} :   {{ entityData.location ? ipLocationRegion(entityData.location) : '-' }}
diff --git a/src/views/entityExplorer/search/ExplorerSearch.vue b/src/views/entityExplorer/search/ExplorerSearch.vue index 529472de..763f87e3 100644 --- a/src/views/entityExplorer/search/ExplorerSearch.vue +++ b/src/views/entityExplorer/search/ExplorerSearch.vue @@ -74,11 +74,11 @@ export default { { value: 'AND', label: 'AND' - }, - { - value: 'OR', - label: 'OR' } + // { + // value: 'OR', + // label: 'OR' + // } ], showHistory: false, history: []