CN-1080: 实体详情--域名解析和开放端口tab接口对接

This commit is contained in:
刘洪洪
2023-06-12 10:51:56 +08:00
parent 0cab29925f
commit 08437a81f2
6 changed files with 321 additions and 166 deletions

View File

@@ -28,6 +28,9 @@
background: #38ACD2;
border-radius: 1px;
}
.type-title-word {
margin-right: 6px;
}
}
.type-content {
@@ -50,5 +53,18 @@
font-weight: 400;
}
}
.type-error-content {
width:100%;
height: 40px;
margin-bottom: 12px;
position: relative;
.type-error-content__block {
position: absolute;
left: -12px;
top: -12px;
}
}
}
}

View File

@@ -352,6 +352,105 @@ if (openMock) {
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/ip/relate/ports.*'), 'get', function (requestObj) {
const result = [
{
port: 80,
l7Protocol: 'HTTP'
},
{
port: 443,
l7Protocol: 'HTTPS'
},
{
port: 53,
l7Protocol: 'DNS'
}
]
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/ip/relate/apps.*'), 'get', function (requestObj) {
const result = ['Wechat', 'baidu', 'Wechat', 'baidu', 'Wechat', 'baidu', 'Wechat']
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/ip/relate/domains.*'), 'get', function (requestObj) {
const result = ['-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com']
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/app/relate/ips.*'), 'get', function (requestObj) {
const result = ['116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242']
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/app/relate/domains.*'), 'get', function (requestObj) {
const result = ['-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com']
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/domain/relate/ips.*'), 'get', function (requestObj) {
const result = ['116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242']
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/domain/relate/apps.*'), 'get', function (requestObj) {
const result = ['Wechat', 'baidu', 'Wechat', 'baidu', 'Wechat', 'baidu', 'Wechat']
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(urlAndVersion + '/entity/detail/domain/relate/fqdns.*'), 'get', function (requestObj) {
const result = ['-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com']
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
}
const getQuery = (url) => {

View File

@@ -7,6 +7,7 @@ import { get, post } from '@/utils/http'
import axios from 'axios'
import { sortByOrderNum } from '@/permission'
import { storageKey } from '@/utils/constants'
const apiVersion = BASE_CONFIG.apiVersion
export const api = {
// 系统相关
@@ -232,16 +233,26 @@ export const api = {
drilldownTrafficAnalysis: '/interface/dns/overview/drilldown/trafficAnalysis'
},
entity: {
throughput: BASE_CONFIG.apiVersion + '/entity/detail/traffic/throughput',
security: BASE_CONFIG.apiVersion + '/entity/detail/event/security',
performance: BASE_CONFIG.apiVersion + '/entity/detail/event/performance',
domainNameResolutionRelatedApp: 'interface/entity/detail/overview/ip/relatedApp',
domainNameResolutionRelatedIP: 'interface/entity/detail/overview/ip/relatedIp',
domainNameResolutionRelatedDomain: 'interface/entity/detail/overview/ip/relatedDomain',
openPort: '',
basicInfo: BASE_CONFIG.apiVersion + '/entity/detail/basic',
tags: BASE_CONFIG.apiVersion + '/entity/detail/kb/intelligence/tag',
informationAggregation: BASE_CONFIG.apiVersion + '/entity/detail/kb/intelligence/list'
throughput: apiVersion + '/entity/detail/traffic/throughput',
security: apiVersion + '/entity/detail/event/security',
performance: apiVersion + '/entity/detail/event/performance',
// 域名解析ip相关app、domain
domainNameResolutionAboutAppsOfIp: apiVersion + '/entity/detail/ip/relate/apps',
domainNameResolutionAboutDomainsOfIp: apiVersion + '/entity/detail/ip/relate/domains',
// 域名解析app相关ip、domain
domainNameResolutionAboutIpsOfApp: apiVersion + '/entity/detail/app/relate/ips',
domainNameResolutionAboutDomainsOfApp: apiVersion + '/entity/detail/app/relate/domains',
// 域名解析domain相关ip、app、fqdn
domainNameResolutionAboutAppsOfDomain: apiVersion + '/entity/detail/domain/relate/apps',
domainNameResolutionAboutIpsOfDomain: apiVersion + '/entity/detail/domain/relate/ips',
domainNameResolutionAboutFQDNsOfDomain: apiVersion + '/entity/detail/domain/relate/fqdns',
// 开放端口ip、domain、app相关
openPortOfIp: apiVersion + '/entity/detail/ip/relate/ports',
openPortOfDomain: apiVersion + '/entity/detail/domain/relate/ports',
openPortOfApp: apiVersion + '/entity/detail/app/relate/ports',
basicInfo: apiVersion + '/entity/detail/basic',
tags: apiVersion + '/entity/detail/kb/intelligence/tag',
informationAggregation: apiVersion + '/entity/detail/kb/intelligence/list'
}
}

View File

@@ -13,11 +13,11 @@
<i v-if="tab.warnFlag" :class="tab.warnIcon" class="tab-pane-warn--icon"></i>
</template>
<information-aggregation v-if="tab.name === entityDetailTabsName.informationAggregation && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity"></information-aggregation>
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.domainNameResolution && tab.name === activeTab" @toggleLoading="setLoading"></domain-name-resolution>
<domain-name-resolution v-else-if="tab.name === entityDetailTabsName.domainNameResolution && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter"></domain-name-resolution>
<digital-certificate v-else-if="tab.name === entityDetailTabsName.digitalCertificate && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" />
<security-event v-else-if="tab.name === entityDetailTabsName.securityEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkWarn="setWarn" />
<performance-event v-else-if="tab.name === entityDetailTabsName.performanceEvent && tab.name === activeTab" @toggleLoading="setLoading" :timeFilter="oneDayTimeFilter" @checkWarn="setWarn" />
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading"></open-port>
<open-port v-else-if="tab.name === entityDetailTabsName.openPort && tab.name === activeTab" @toggleLoading="setLoading" :entity="entity" :timeFilter="oneDayTimeFilter"></open-port>
</el-tab-pane>
</el-tabs>
</div>
@@ -80,8 +80,8 @@ export default {
const entityType = props.entity.entityType
const tabs = reactive([
// { name: entityDetailTabsName.domainNameResolution, label: i18n.global.t('entities.domainNameResolution'), icon: 'cn-icon cn-icon-domain-name-resolution' },
// { name: entityDetailTabsName.openPort, label: i18n.global.t('entities.openPort'), icon: 'cn-icon cn-icon-open-port' },
{ name: entityDetailTabsName.domainNameResolution, label: i18n.global.t('entities.domainNameResolution'), icon: 'cn-icon cn-icon-domain-name-resolution' },
{ name: entityDetailTabsName.openPort, label: i18n.global.t('entities.openPort'), icon: 'cn-icon cn-icon-open-port' },
// { name: entityDetailTabsName.digitalCertificate, label: i18n.global.t('entities.digitalCertificate'), icon: 'cn-icon cn-icon-digital-certificate' },
{ name: entityDetailTabsName.securityEvent, label: i18n.global.t('overall.securityEvent'), icon: 'cn-icon cn-icon-security-event', warnIcon: 'cn-icon cn-icon-warn', warnFlag: false },
{ name: entityDetailTabsName.performanceEvent, label: i18n.global.t('overall.performanceEvent'), icon: 'cn-icon cn-icon-a-PerformanceEvent', warnIcon: 'cn-icon cn-icon-warn', warnFlag: false }

View File

@@ -1,162 +1,200 @@
<template>
<div>
<chart-error v-if="showError" :content="errorMsg" class="entity-detail-event-error"></chart-error>
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
<chart-no-data v-if="isNoData"></chart-no-data>
<div v-if="!isNoData && initFlag" class="type-data__column">
<div class="type-data" v-if="entity.entityType !== 'app'">
<div class="type-title">
<span class="title-mark"></span>
{{ $t('entities.tab.relatedApp') }}&nbsp;&nbsp;({{ relatedAppList.length }})
</div>
<div class="type-content" v-if="!showError0">
<div v-for="(entity, index) in relatedAppList" class="data-item" :key="index">
{{ entity.appName ? entity.appName : entity }}
</div>
</div>
<div class="type-error-content" v-else>
<chart-error class="type-error-content__block" :content="errorMsg0"></chart-error>
</div>
</div>
<div class="type-data" v-if="entity.entityType !== 'ip'">
<div class="type-title">
<span class="title-mark"></span>
{{ $t('entities.tab.relatedIp') }}&nbsp;&nbsp;({{ relatedIpList.length }})
</div>
<div class="type-content" v-if="!showError1">
<div v-for="(entity, index) in relatedIpList" :key="index" class="data-item">
{{ entity.ip ? entity.ip : entity }}
</div>
</div>
<div class="type-error-content" v-else>
<chart-error class="type-error-content__block" :content="errorMsg1"></chart-error>
</div>
</div>
<div v-if="!isNoData && !showError && !loading" class="type-data__column">
<div class="type-data">
<div class="type-title">
<span class="title-mark" ></span>{{ $t('entities.tab.relatedApp') }}&nbsp;&nbsp;({{relatedAppList.length}})</div>
<div class="type-content">
<div v-for="entity in relatedAppList" class="data-item">{{entity.appName?entity.appName:entity}}</div>
<span class="title-mark"></span>
{{ $t('entities.tab.relatedDomain') }}&nbsp;&nbsp;({{ relatedDomainList.length }})
</div>
<div class="type-content" v-if="!showError2">
<div v-for="(entity, index) in relatedDomainList" class="data-item" :key="index">
{{ entity.domain ? entity.domain : entity }}
</div>
</div>
<div class="type-data">
<div class="type-title">
<span class="title-mark" ></span>{{ $t('entities.tab.relatedIp') }}&nbsp;&nbsp;({{relatedIpList.length}})</div>
<div class="type-content">
<div v-for="entity in relatedIpList" class="data-item">{{entity.ip?entity.ip:entity}}</div>
</div>
</div>
<div class="type-data">
<div class="type-title">
<span class="title-mark" ></span>{{ $t('entities.tab.relatedDomain') }}&nbsp;&nbsp;({{relatedDomainList.length}})</div>
<div class="type-content">
<div v-for="entity in relatedDomainList" class="data-item">{{entity.domain?entity.domain:entity}}</div>
<div class="type-error-content" v-else>
<chart-error class="type-error-content__block" :content="errorMsg2"></chart-error>
</div>
</div>
</div>
</div>
</template>
<script>
import { getNowTime, getSecond } from '@/utils/date-util'
import axios from 'axios'
import { api } from '@/utils/api'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import chartMixin from '@/views/charts2/chart-mixin'
import { getSecond } from '@/utils/date-util'
import chartNoData from '@/views/charts/charts/ChartNoData'
export default {
name: 'DomainNameResolution',
mixins: [chartMixin],
props: {
},
components: {
chartNoData
},
data () {
return {
relatedAppList: [],
relatedIpList: [],
relatedDomainList: [],
showError: false,
errorMsg: '',
loading: true
initFlag: false, // 初始化标识,请求接口之后再显示,避免标题初始化会闪一下
showError0: false,
errorMsg0: '',
showError1: false,
errorMsg1: '',
showError2: false,
errorMsg2: ''
}
},
mounted () {
this.initData()
},
setup () {
const { query } = useRoute()
// 获取url携带的range、startTime、endTime
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const {
startTime,
endTime
} = getNowTime(60)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
} else {
timeFilter.value.startTime = parseInt(startTimeParam)
timeFilter.value.endTime = parseInt(endTimeParam)
}
return {
timeFilter
}
},
methods: {
initData () {
const params = {
resource: this.entity.entityName,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime),
ip: '116.178.70.242'
endTime: getSecond(this.timeFilter.endTime)
}
this.loading = true
this.toggleLoading(true)
const relatedApp = new Promise(resolve => {
axios.get(api.entity.domainNameResolutionRelatedApp, { params: params }).then(response => {
const res = response.data
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (!this.isNoData) {
this.relatedAppList = res.data.result
if (this.entity.entityType === 'app') {
const ipsOfApp = axios.get(api.entity.domainNameResolutionAboutIpsOfApp, { params: params })
const domainsOfApp = axios.get(api.entity.domainNameResolutionAboutDomainsOfApp, { params: params })
this.promiseData(ipsOfApp, domainsOfApp)
}
} else {
this.httpError(res)
if (this.entity.entityType === 'ip') {
const appsOfIp = axios.get(api.entity.domainNameResolutionAboutAppsOfIp, { params: params })
const domainsOfIp = axios.get(api.entity.domainNameResolutionAboutDomainsOfIp, { params: params })
this.promiseData(appsOfIp, domainsOfIp)
}
}).catch(e => {
// console.error(e)
this.httpError(e)
}).finally(() => {
// this.toggleLoading(false)
resolve()
})
})
const relatedIp = new Promise(resolve => {
axios.get(api.entity.domainNameResolutionRelatedIP, { params: params }).then(response => {
const res = response.data
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (!this.isNoData) {
this.relatedIpList = res.data.result
if (this.entity.entityType === 'domain') {
const appsOfDomain = axios.get(api.entity.domainNameResolutionAboutAppsOfDomain, { params: params })
const ipsOfDomain = axios.get(api.entity.domainNameResolutionAboutIpsOfDomain, { params: params })
const fqdnsOfDomain = axios.get(api.entity.domainNameResolutionAboutFQDNsOfDomain, { params: params })
this.promiseData(appsOfDomain, ipsOfDomain, fqdnsOfDomain)
}
} else {
this.httpError(res)
}
}).catch(e => {
// console.error(e)
this.httpError(e)
}).finally(() => {
resolve()
})
})
const relatedDomain = new Promise(resolve => {
axios.get(api.entity.domainNameResolutionRelatedDomain, { params: params }).then(response => {
const res = response.data
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
this.showError = false
if (!this.isNoData) {
this.relatedDomainList = res.data.result
}
} else {
this.httpError(res)
}
}).catch(e => {
// console.error(e)
this.httpError(e)
}).finally(() => {
resolve()
})
})
Promise.all([relatedApp, relatedIp, relatedDomain]).finally(response => {
this.toggleLoading(false)
this.loading = false
})
},
httpError (e) {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
this.relatedAppList = ['Wechat', 'baidu', 'Wechat', 'baidu', 'Wechat', 'baidu', 'Wechat']
this.relatedIpList = ['116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242', '116.178.70.242']
this.relatedDomainList = ['-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com', '-*.pangolin-sdk-toutiao.com']
promiseData (data1, data2, data3) {
this.showError0 = false
this.showError1 = false
this.showError2 = false
this.toggleLoading(true)
Promise.all([data1, data2, data3]).then(res => {
const res0 = res[0].data
const res1 = res[1].data
if (res0.code === 200 && res1.code === 200) {
this.isNoData = res0.data.result.length === 0 && res1.data.result.length === 0
}
// app相关显示ip、domain
if (this.entity.entityType === 'app' && !this.isNoData) {
if (res0.code === 200) {
this.relatedIpList = res0.data.result
} else {
this.showError1 = true
this.errorMsg1 = this.errorMsgHandler(res0)
}
if (res1.code === 200) {
this.relatedDomainList = res1.data.result
} else {
this.showError2 = true
this.errorMsg2 = this.errorMsgHandler(res1)
}
}
// ip相关显示appdomain
if (this.entity.entityType === 'ip' && !this.isNoData) {
if (res0.code === 200) {
this.relatedAppList = res0.data.result
} else {
this.showError0 = true
this.errorMsg0 = this.errorMsgHandler(res0)
}
if (res1.code === 200) {
this.relatedDomainList = res1.data.result
} else {
this.showError2 = true
this.errorMsg2 = this.errorMsgHandler(res1)
}
}
// domain相关显示appipdomain
if (this.entity.entityType === 'domain') {
const res2 = res[2].data
if (res0.code === 200 && res1.code === 200 && res2.code === 200) {
this.isNoData = res0.data.result.length === 0 && res1.data.result.length === 0 && res2.data.result.length === 0
}
if (res0.code === 200) {
this.relatedAppList = res0.data.result
} else {
this.showError0 = true
this.errorMsg0 = this.errorMsgHandler(res0)
}
if (res1.code === 200) {
this.relatedIpList = res1.data.result
} else {
this.showError1 = true
this.errorMsg1 = this.errorMsgHandler(res1)
}
if (res2.code === 200) {
this.relatedDomainList = res2.data.result
} else {
this.showError2 = true
this.errorMsg2 = this.errorMsgHandler(res2)
}
}
}).catch(e => {
console.log(e)
this.showError0 = true
this.showError1 = true
this.showError2 = true
this.errorMsg0 = this.errorMsgHandler(e)
this.errorMsg1 = this.errorMsgHandler(e)
this.errorMsg2 = this.errorMsgHandler(e)
}).finally(() => {
this.initFlag = true
this.toggleLoading(false)
})
}
}
}

View File

@@ -3,24 +3,27 @@
<chart-error v-if="showError" :content="errorMsg" class="entity-detail-event-error"></chart-error>
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div v-if="!showError && !isNoData && !loading" class="type-data__column">
<div v-if="!showError && !isNoData && initFlag" class="type-data__column">
<div class="type-data">
<div class="type-title">
<span class="title-mark" ></span>{{ $t('entities.tab.currentDevelopmentPortsAndServices') }}&nbsp;&nbsp;(5)</div>
<span class="title-mark"></span>
<span class="type-title-word">{{ $t('entities.tab.currentDevelopmentPortsAndServices') }}</span>({{ openPortList.length }})
</div>
<div class="type-content">
<div v-for="openPort in openPortList" class="data-item">{{openPort}}</div>
<div v-for="(openPort, index) in openPortList" :key="index" class="data-item">
{{ openPort.port }}/{{ openPort.l7Protocol }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getNowTime, getSecond } from '@/utils/date-util'
import axios from 'axios'
import { api } from '@/utils/api'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import chartMixin from '@/views/charts2/chart-mixin'
import { api } from '@/utils/api'
import { getSecond } from '@/utils/date-util'
import chartNoData from '@/views/charts/charts/ChartNoData'
export default {
name: 'OpenPort',
@@ -32,46 +35,28 @@ export default {
openPortList: [],
showError: false,
errorMsg: '',
loading: true
initFlag: false // 初始化标识,请求接口之后再显示,避免标题初始化会闪一下
}
},
components: {
chartNoData
},
mounted () {
this.initData()
},
setup () {
const { query } = useRoute()
// 获取url携带的range、startTime、endTime
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
// 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const {
startTime,
endTime
} = getNowTime(60)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
} else {
timeFilter.value.startTime = parseInt(startTimeParam)
timeFilter.value.endTime = parseInt(endTimeParam)
}
return {
timeFilter
}
},
methods: {
initData () {
const params = {
resource: this.entity.entityName,
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
this.toggleLoading(true)
this.loading = true
axios.get(api.entity.openPort, { params: params }).then(response => {
const url = this.getUrlByEntityType(this.entity.entityType)
axios.get(url, { params: params }).then(response => {
const res = response.data
if (res.code === 200) {
this.isNoData = res.data.result.length === 0
@@ -86,15 +71,21 @@ export default {
// console.error(e)
this.httpError(e)
}).finally(() => {
this.initFlag = true
this.toggleLoading(false)
this.loading = false
})
},
httpError (e) {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
this.openPortList = ['11/rpcbind', '22/rpcbind', '231/zmpt', '22/rpcbind', '231/zmpt']
},
getUrlByEntityType (type) {
switch (type) {
case 'ip': return api.entity.openPortOfIp
case 'domain': return api.entity.openPortOfDomain
case 'app': return api.entity.openPortOfApp
}
}
}
}