| 1 | <template> |
| 2 | <div> |
| 3 | <CardEntity hoverable clickable :embedded class="@container" @click.stop="showDetails = true"> |
| 4 | <template #headerMain>#{{ alert._id || alert._source.id }}</template> |
| 5 | <template #headerExtra> |
| 6 | {{ formatDate(alert._source.timestamp_utc, dFormats.datetimesec) }} |
| 7 | </template> |
| 8 | <template #default> |
| 9 | <div class="flex flex-col gap-1"> |
| 10 | {{ alert._source.rule_description || alert._source.type }} |
| 11 | <p> |
| 12 | {{ alert._source.rule_groups }} |
| 13 | </p> |
| 14 | </div> |
| 15 | </template> |
| 16 | <template v-if="alert._id" #mainExtra> |
| 17 | <div class="flex flex-wrap items-center gap-3"> |
| 18 | <Badge type="splitted" color="primary"> |
| 19 | <template #iconLeft> |
| 20 | <Icon :name="TargetIcon" :size="13" class="opacity-80!" /> |
| 21 | </template> |
| 22 | <template #label>Fired times</template> |
| 23 | <template #value> |
| 24 | {{ alert._source.rule_firedtimes }} |
| 25 | </template> |
| 26 | </Badge> |
| 27 | |
| 28 | <Badge :type="alert._source.rule_mail ? 'active' : 'muted'"> |
| 29 | <template #iconRight> |
| 30 | <Icon :name="alert._source.rule_mail ? MailIcon : DisabledIcon" :size="14" /> |
| 31 | </template> |
| 32 | <template #label>Rule mail</template> |
| 33 | </Badge> |
| 34 | |
| 35 | <n-popover overlap placement="bottom-start"> |
| 36 | <template #trigger> |
| 37 | <Badge type="splitted" color="primary" hint-cursor> |
| 38 | <template #iconLeft> |
| 39 | <Icon :name="AgentIcon" :size="13" class="opacity-80!" /> |
| 40 | </template> |
| 41 | <template #label>Agent</template> |
| 42 | <template #value> |
| 43 | <div class="flex flex-wrap items-center gap-2"> |
| 44 | {{ alert._source.agent_name }} / {{ alert._source.agent_labels_customer }} |
| 45 | <Icon :name="InfoIcon" :size="13" class="opacity-80!" /> |
| 46 | </div> |
| 47 | </template> |
| 48 | </Badge> |
| 49 | </template> |
| 50 | <div class="flex flex-col gap-1"> |
| 51 | <div class="box"> |
| 52 | agent_id: |
| 53 | <code |
| 54 | class="text-primary cursor-pointer" |
| 55 | @click.stop="routeAgent(alert._source.agent_id).navigate()" |
| 56 | > |
| 57 | {{ alert._source.agent_id }} |
| 58 | <Icon :name="LinkIcon" :size="13" class="relative top-0.5" /> |
| 59 | </code> |
| 60 | </div> |
| 61 | <div class="box"> |
| 62 | agent_ip: |
| 63 | <code>{{ alert._source.agent_ip }}</code> |
| 64 | </div> |
| 65 | <div class="box"> |
| 66 | agent_name: |
| 67 | <code>{{ alert._source.agent_name }}</code> |
| 68 | </div> |
| 69 | <div class="box"> |
| 70 | agent_labels_customer: |
| 71 | <code |
| 72 | class="text-primary cursor-pointer" |
| 73 | @click.stop=" |
| 74 | routeCustomer({ code: alert._source.agent_labels_customer }).navigate() |
| 75 | " |
| 76 | > |
| 77 | {{ alert._source.agent_labels_customer }} |
| 78 | <Icon :name="LinkIcon" :size="13" class="relative top-0.5" /> |
| 79 | </code> |
| 80 | </div> |
| 81 | </div> |
| 82 | </n-popover> |
| 83 | <Badge type="splitted" color="primary"> |
| 84 | <template #label>syslog</template> |
| 85 | <template #value>{{ alert._source.syslog_type }} / {{ alert._source.syslog_level }}</template> |
| 86 | </Badge> |
| 87 | <Badge type="splitted" color="primary" class="hidden! @2xl:flex!"> |
| 88 | <template #label>manager</template> |
| 89 | <template #value> |
| 90 | {{ alert._source.manager_name }} |
| 91 | </template> |
| 92 | </Badge> |
| 93 | <Badge type="splitted" color="primary" class="hidden! @2xl:flex!"> |
| 94 | <template #label>decoder</template> |
| 95 | <template #value> |
| 96 | {{ alert._source.decoder_name }} |
| 97 | </template> |
| 98 | </Badge> |
| 99 | <Badge type="splitted" color="primary" class="hidden! @2xl:flex!"> |
| 100 | <template #label>source</template> |
| 101 | <template #value> |
| 102 | {{ alert._source.source }} |
| 103 | </template> |
| 104 | </Badge> |
| 105 | </div> |
| 106 | </template> |
| 107 | <template v-if="!hideActions" #footerExtra> |
| 108 | <AlertActions |
| 109 | :alert |
| 110 | :soc-alert-field="socAlertCreationField" |
| 111 | size="small" |
| 112 | @start-loading="loading = true" |
| 113 | @stop-loading="loading = false" |
| 114 | @updated-url="alert._source.alert_url = $event" |
| 115 | @updated-id="alert._source.alert_id = $event" |
| 116 | @updated-ask-message="alert._source.ask_socfortress_message = $event" |
| 117 | /> |
| 118 | </template> |
| 119 | </CardEntity> |
| 120 | |
| 121 | <n-modal |
| 122 | v-model:show="showDetails" |
| 123 | preset="card" |
| 124 | content-class="p-0!" |
| 125 | :style="{ maxWidth: 'min(800px, 90vw)', minHeight: 'min(600px, 90vh)', overflow: 'hidden' }" |
| 126 | :title="`Alert: ${alert._id || alert._source.id}`" |
| 127 | :bordered="false" |
| 128 | segmented |
| 129 | > |
| 130 | <n-tabs type="line" animated :tabs-padding="24"> |
| 131 | <n-tab-pane v-if="alert._id" name="Agent" tab="Agent" display-directive="show"> |
| 132 | <div v-if="agentProperties" class="grid-auto-fit-200 grid gap-2 p-6 pt-3"> |
| 133 | <CardKV v-for="(value, key) of agentProperties" :key> |
| 134 | <template #key> |
| 135 | {{ key }} |
| 136 | </template> |
| 137 | <template #value> |
| 138 | <template v-if="key === 'agent_id'"> |
| 139 | <code |
| 140 | class="text-primary cursor-pointer" |
| 141 | @click.stop="routeAgent(`${value}`).navigate()" |
| 142 | > |
| 143 | {{ value }} |
| 144 | <Icon :name="LinkIcon" :size="13" class="relative top-0.5" /> |
| 145 | </code> |
| 146 | </template> |
| 147 | <template v-else-if="key === 'agent_labels_customer'"> |
| 148 | <code |
| 149 | class="text-primary cursor-pointer" |
| 150 | @click.stop=" |
| 151 | routeCustomer(value ? { code: value.toString() } : undefined).navigate() |
| 152 | " |
| 153 | > |
| 154 | {{ value }} |
| 155 | <Icon :name="LinkIcon" :size="13" class="relative top-0.5" /> |
| 156 | </code> |
| 157 | </template> |
| 158 | <template v-else> |
| 159 | {{ value || "-" }} |
| 160 | </template> |
| 161 | </template> |
| 162 | </CardKV> |
| 163 | </div> |
| 164 | </n-tab-pane> |
| 165 | <n-tab-pane |
| 166 | v-if="alert._source.ask_socfortress_message" |
| 167 | name="SOCFortress Response" |
| 168 | tab="SOCFortress Response" |
| 169 | display-directive="show" |
| 170 | > |
| 171 | <div class="p-6 pt-3"> |
| 172 | <n-input |
| 173 | :value="alert._source.ask_socfortress_message" |
| 174 | type="textarea" |
| 175 | readonly |
| 176 | placeholder="Empty" |
| 177 | size="large" |
| 178 | :autosize="{ |
| 179 | minRows: 3 |
| 180 | }" |
| 181 | /> |
| 182 | </div> |
| 183 | </n-tab-pane> |
| 184 | <n-tab-pane v-if="alert._source.message" name="Message" tab="Message" display-directive="show"> |
| 185 | <div class="p-6 pt-3"> |
| 186 | <CodeSource :code="alert._source.message" :decode="false" /> |
| 187 | </div> |
| 188 | </n-tab-pane> |
| 189 | <n-tab-pane |
| 190 | v-if="alert._source.data_document" |
| 191 | name="Data document" |
| 192 | tab="Data document" |
| 193 | display-directive="show" |
| 194 | > |
| 195 | <div class="p-6 pt-3"> |
| 196 | <n-input |
| 197 | :value="alert._source.data_document" |
| 198 | type="textarea" |
| 199 | readonly |
| 200 | placeholder="Empty" |
| 201 | size="large" |
| 202 | :autosize="{ |
| 203 | minRows: 3 |
| 204 | }" |
| 205 | /> |
| 206 | </div> |
| 207 | </n-tab-pane> |
| 208 | <n-tab-pane name="Details" tab="Details" display-directive="show:lazy"> |
| 209 | <div class="p-6 pt-3"> |
| 210 | <CodeSource :code="alert._source" lang="json" :decode="false" /> |
| 211 | </div> |
| 212 | </n-tab-pane> |
| 213 | </n-tabs> |
| 214 | </n-modal> |
| 215 | </div> |
| 216 | </template> |
| 217 | |
| 218 | <script setup lang="ts"> |
| 219 | import type { SocAlertField } from "./type.d" |
| 220 | import type { Alert } from "@/types/alerts.d" |
| 221 | import _pick from "lodash/pick" |
| 222 | import { NInput, NModal, NPopover, NTabPane, NTabs } from "naive-ui" |
| 223 | import { computed, defineAsyncComponent, inject, ref, toRefs } from "vue" |
| 224 | import Badge from "@/components/common/Badge.vue" |
| 225 | import CardEntity from "@/components/common/cards/CardEntity.vue" |
| 226 | import CardKV from "@/components/common/cards/CardKV.vue" |
| 227 | import Icon from "@/components/common/Icon.vue" |
| 228 | import { useNavigation } from "@/composables/useNavigation" |
| 229 | import { useSettingsStore } from "@/stores/settings" |
| 230 | import { formatDate } from "@/utils/format" |
| 231 | |
| 232 | const props = defineProps<{ alert: Alert; hideActions?: boolean; embedded?: boolean }>() |
| 233 | const AlertActions = defineAsyncComponent(() => import("./AlertActions.vue")) |
| 234 | const CodeSource = defineAsyncComponent(() => import("@/components/common/CodeSource.vue")) |
| 235 | |
| 236 | const { alert, hideActions, embedded } = toRefs(props) |
| 237 | |
| 238 | const InfoIcon = "carbon:information" |
| 239 | const TargetIcon = "zondicons:target" |
| 240 | const DisabledIcon = "carbon:subtract" |
| 241 | const MailIcon = "carbon:email" |
| 242 | const AgentIcon = "carbon:police" |
| 243 | const LinkIcon = "carbon:launch" |
| 244 | |
| 245 | const { routeCustomer, routeAgent } = useNavigation() |
| 246 | const loading = ref(false) |
| 247 | const showDetails = ref(false) |
| 248 | const dFormats = useSettingsStore().dateFormat |
| 249 | |
| 250 | const agentProperties = computed(() => { |
| 251 | return _pick(alert.value._source, [ |
| 252 | "agent_id", |
| 253 | "agent_ip_city_name", |
| 254 | "agent_ip_country_code", |
| 255 | "agent_ip_geolocation", |
| 256 | "agent_ip_reserved_ip", |
| 257 | "agent_ip", |
| 258 | "agent_labels_customer", |
| 259 | "agent_name" |
| 260 | ]) |
| 261 | }) |
| 262 | |
| 263 | const socAlertCreationField = ref(inject<SocAlertField>("soc-alert-creation-field", "alert_url")) |
| 264 | </script> |