CN-988: 实体详情--安全事件和性能事件tab开发

This commit is contained in:
刘洪洪
2023-05-05 14:49:59 +08:00
parent 492a18dbac
commit fe0ac008fa
8 changed files with 440 additions and 6 deletions

View File

@@ -107,6 +107,9 @@
letter-spacing: 0; letter-spacing: 0;
line-height: 14px; line-height: 14px;
margin-left: 5px; margin-left: 5px;
font-style: italic;
padding: 0 2px;
font-weight: 500;
} }
.detection-event-severity-color-block { .detection-event-severity-color-block {
width: 5px; width: 5px;
@@ -143,12 +146,12 @@
flex-wrap: wrap; flex-wrap: wrap;
.basic-info__item { .basic-info__item {
padding-right: 40px; padding-right: 30px;
display: flex; display: flex;
align-items: center; align-items: center;
i { i {
padding-right: 6px; padding-right: 5px;
color: #8FA1BE; color: #8FA1BE;
font-size: 14px; font-size: 14px;
} }

View File

@@ -87,7 +87,7 @@
flex-wrap: wrap; flex-wrap: wrap;
.basic-info__item { .basic-info__item {
padding-right: 40px; padding-right: 30px;
.item__box { .item__box {
display: flex; display: flex;
@@ -105,7 +105,7 @@
} }
i { i {
padding-right: 6px; padding-right: 5px;
color: #8FA1BE; color: #8FA1BE;
font-size: 12px; font-size: 12px;
height: 13px; height: 13px;

View File

@@ -66,6 +66,84 @@ if (openMock) {
} }
} }
}) })
Mock.mock(new RegExp(BASE_CONFIG.baseUrl + 'interface/entityDetail/securityEvent.*'), 'get', function (requestObj) {
const result = [
{
eventId: '1298414830886991872',
securityType: 'command and control',
domain: null,
offenderIp: '213.186.33.5',
victimIp: '116.178.217.92',
offenderDomain: 'baidu.com',
victimDomain: 'mi.com',
eventSeverity: 'Critical',
malwareName: 'NetWire RC',
cryptominingPool: null,
startTime: 1683186600,
durationMs: 300000,
endTime: 1683186900
},
{
eventId: '1298414830886991873',
securityType: 'command and control',
domain: null,
offenderIp: '213.186.33.5',
victimIp: '116.178.217.93',
offenderDomain: 'baidu.com',
victimDomain: 'mi.com',
eventSeverity: 'Low',
malwareName: 'NetWire RC',
cryptominingPool: null,
startTime: 1683186600,
durationMs: 300000,
endTime: 1683186900
}
]
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
Mock.mock(new RegExp(BASE_CONFIG.baseUrl + 'interface/entityDetail/performanceEvent.*'), 'get', function (requestObj) {
const result = [
{
eventId: '1308078720390412288',
entityType: 'ip',
serverIp: '116.178.78.180',
domain: null,
appName: null,
eventSeverity: 'Critical',
eventType: 'Http error',
startTime: 1683250500,
durationMs: 900000,
endTime: 1683251400
},
{
eventId: '1308078720390412289',
entityType: 'ip',
serverIp: '116.178.78.180',
domain: null,
appName: null,
eventSeverity: 'Info',
eventType: 'Http error',
startTime: 1683250500,
durationMs: 900000,
endTime: 1683251400
}
]
return {
msg: 'success',
code: 200,
data: {
result: result
}
}
})
} }
const getQuery = (url) => { const getQuery = (url) => {

View File

@@ -234,7 +234,9 @@ export const api = {
drilldownTrafficAnalysis: '/interface/dns/overview/drilldown/trafficAnalysis' drilldownTrafficAnalysis: '/interface/dns/overview/drilldown/trafficAnalysis'
}, },
entity: { entity: {
totalTrafficAnalysis: 'interface/entityDetail/totalTrafficAnalysis' totalTrafficAnalysis: 'interface/entityDetail/totalTrafficAnalysis',
securityEvent: 'interface/entityDetail/securityEvent',
performanceEvent: 'interface/entityDetail/performanceEvent'
} }
} }

View File

@@ -165,6 +165,13 @@ export const eventSeverityColor = {
low: '#FFD82D', low: '#FFD82D',
info: '#D1BD50' info: '#D1BD50'
} }
export const eventSeverityColor1 = {
Critical: '#D84C4C',
High: '#FE845D',
Medium: '#FFB65A',
Low: '#FFD82D',
Info: '#D1BD50'
}
export const securityType = { export const securityType = {
commandAndControl: 'common and control', commandAndControl: 'common and control',
payloadDelivery: 'payload delivery', payloadDelivery: 'payload delivery',

View File

@@ -0,0 +1,160 @@
<template>
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div
class="detection-border"
v-for="item in eventList"
:key="item.eventId"
style="margin-bottom: 10px;">
<div class="cn-detection--list">
<div class="cn-detection__case" style="height: 46px">
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor1[item.eventSecurity]}`"></div>
<div class="cn-detection__row">
<div class="cn-detection__header">
<span
class="detection-event-severity-color-block"
:style="`background-color: ${eventSeverityColor1[item.eventSeverity]}`">
</span>
<span class="detection-event-severity-block" style="margin-right: 30px">
{{ item.eventType || '-' }}
</span>
<div class="cn-detection__body">
<div class="body__basic-info">
<div class="basic-info">
<div class="basic-info__item" v-if="item.eventSeverity">
<i class="cn-icon cn-icon-severity-level"></i>
<span>{{ $t('network.severity') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ item.eventSeverity || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-time2"></i>
<span>{{ $t('detection.list.startTime') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ dateFormatByAppearance(item.startTime) || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-Duration"></i>
<span>{{ $t('overall.duration') }}&nbsp;:&nbsp;&nbsp;&nbsp;</span>
<span>{{ unitConvert(item.durationMs, 'time', null, null, 0).join(' ') || '-' }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getMillisecond, getNowTime, getSecond } from '@/utils/date-util'
import { eventSeverityColor1 } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from '_axios@0.21.4@axios'
import { api } from '@/utils/api'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import chartMixin from '@/views/charts2/chart-mixin'
export default {
name: 'PerformanceEvent',
mixins: [chartMixin],
data () {
return {
eventList: [],
showError: false,
eventSeverityColor1
}
},
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
}
},
mounted () {
this.initData()
},
computed: {
pointColor () {
return function (item) {
let color = '#8FA1BE'
if (item.startTime && item.endTime) {
if (getMillisecond(item.endTime) - getMillisecond(item.startTime) < 5 * 60 * 1000) {
color = '#D84C4C'
}
}
return { backgroundColor: color }
}
}
},
methods: {
unitConvert,
initData () {
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
this.toggleLoading(true)
axios.get(api.entity.performanceEvent, { 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.eventList = res.data.result
}
} else {
this.httpError(res)
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})
},
httpError (e) {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
}
}
}
</script>
<style scoped>
.detection-border {
border: 1px solid #E2E5EC;
border-radius: 4px;
}
.basic-info__item-duration {
display: inline-block;
height: 6px;
width: 6px;
border-radius: 50%;
margin-right: 8px;
}
</style>

View File

@@ -0,0 +1,184 @@
<template>
<chart-no-data v-if="isNoData && !showError"></chart-no-data>
<div
class="detection-border"
v-for="item in eventList"
:key="item.eventId"
style="margin-bottom: 10px;">
<div class="cn-detection--list">
<div class="cn-detection__case" style="height: 70px">
<div class="cn-detection__icon" :style="`background-color: ${eventSeverityColor1[item.eventSecurity]}`"></div>
<div class="cn-detection__row">
<div class="cn-detection__header">
<span
class="detection-event-severity-color-block"
:style="`background-color: ${eventSeverityColor1[item.eventSeverity]}`">
</span>
<span class="detection-event-severity-block">{{ item.securityType || '-' }}</span>
<i class="cn-icon cn-icon-attacker"></i>{{ item.offenderIp || '-' }}
<div class="domain">{{ item.offenderDomain }}</div>
<span class="line">-------</span>
<span class="circle"></span>
<i class="cn-icon cn-icon-attacked"></i>{{ item.victimIp || '-' }}
<div class="domain">{{ item.victimDomain }}</div>
</div>
<div class="cn-detection__body">
<div class="body__basic-info">
<div class="basic-info">
<div class="basic-info__item" v-if="item.eventSecurity">
<i class="cn-icon cn-icon-severity-level"></i>
<span>{{ $t('detection.list.eventSecurity') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ item.eventSecurity || '-' }}</span>
</div>
<div class="basic-info__item" v-if="item.eventSeverity">
<i class="cn-icon cn-icon-severity-level"></i>
<span>{{ $t('network.severity') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ item.eventSeverity || '-' }}</span>
</div>
<div class="basic-info__item" v-if="item.eventType">
<i class="cn-icon cn-icon-event-type"></i>
<span>{{ $t('detections.eventType') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ item.eventType || '-' }}</span>
</div>
<div class="basic-info__item" v-if="item.malwareName">
<i class="cn-icon cn-icon-trojan"></i>
<span>{{ $t('detection.list.malwareName') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ item.malwareName || '-' }}</span>
</div>
<div class="basic-info__item" v-if="item.cryptominingPool">
<i class="cn-icon cn-icon-mining-pool"></i>
<span>{{ $t('detection.list.cryptominingPool') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ item.cryptominingPool || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-time2"></i>
<span>{{ $t('detection.list.startTime') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ dateFormatByAppearance(item.startTime) || '-' }}</span>
</div>
<div class="basic-info__item">
<i class="cn-icon cn-icon-Duration"></i>
<span>{{ $t('overall.duration') }}&nbsp;:&nbsp;&nbsp;</span>
<span>{{ unitConvert(item.durationMs, 'time', null, null, 0).join(' ') || '-' }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { getMillisecond, getNowTime, getSecond } from '@/utils/date-util'
import { eventSeverityColor1 } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import axios from '_axios@0.21.4@axios'
import { api } from '@/utils/api'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import chartMixin from '@/views/charts2/chart-mixin'
export default {
name: 'SecurityEvent',
mixins: [chartMixin],
data () {
return {
eventList: [],
showError: false,
eventSeverityColor1
}
},
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
}
},
mounted () {
this.initData()
},
computed: {
pointColor () {
return function (item) {
let color = '#8FA1BE'
if (item.startTime && item.endTime) {
if (getMillisecond(item.endTime) - getMillisecond(item.startTime) < 5 * 60 * 1000) {
color = '#D84C4C'
}
}
return { backgroundColor: color }
}
}
},
methods: {
unitConvert,
initData () {
const params = {
startTime: getSecond(this.timeFilter.startTime),
endTime: getSecond(this.timeFilter.endTime)
}
this.toggleLoading(true)
axios.get(api.entity.securityEvent, { 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.eventList = res.data.result
}
} else {
this.httpError(res)
}
}).catch(e => {
console.error(e)
this.httpError(e)
}).finally(() => {
this.toggleLoading(false)
})
},
httpError (e) {
this.isNoData = false
this.showError = true
this.errorMsg = this.errorMsgHandler(e)
}
}
}
</script>
<style scoped>
.detection-border {
border: 1px solid #E2E5EC;
border-radius: 4px;
}
.basic-info__item-duration {
display: inline-block;
height: 6px;
width: 6px;
border-radius: 50%;
margin-right: 8px;
}
</style>

View File

@@ -61,7 +61,7 @@
<span>{{dateFormatByAppearance(detection.startTime) || '-'}}</span> <span>{{dateFormatByAppearance(detection.startTime) || '-'}}</span>
</div> </div>
<div class="basic-info__item"> <div class="basic-info__item">
<i class="cn-icon cn-icon-time2"></i> <i class="cn-icon cn-icon-Duration"></i>
<span>{{$t('overall.duration')}}&nbsp;:&nbsp;&nbsp;</span> <span>{{$t('overall.duration')}}&nbsp;:&nbsp;&nbsp;</span>
<span style="display: inline-block;height: 6px;width: 6px;border-radius: 50%;margin-right: 8px;" <span style="display: inline-block;height: 6px;width: 6px;border-radius: 50%;margin-right: 8px;"
:style="pointColor(detection)"></span> :style="pointColor(detection)"></span>