alerts overview work

This commit is contained in:
sadnub
2021-01-25 18:01:25 -05:00
parent 3fe83f81be
commit db5ff372a4
12 changed files with 451 additions and 147 deletions

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.4 on 2021-01-24 18:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('alerts', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='alert',
name='snoozed',
field=models.BooleanField(default=False),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.1.4 on 2021-01-24 18:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('alerts', '0002_alert_snoozed'),
]
operations = [
migrations.AddField(
model_name='alert',
name='alert_type',
field=models.CharField(choices=[('availability', 'Availability'), ('check', 'Check'), ('task', 'Task'), ('custom', 'Custom')], default='availability', max_length=20),
),
]

View File

@@ -8,6 +8,13 @@ SEVERITY_CHOICES = [
("error", "Error"), ("error", "Error"),
] ]
ALERT_TYPE_CHOICES = [
("availability", "Availability"),
("check", "Check"),
("task", "Task"),
("custom", "Custom"),
]
class Alert(models.Model): class Alert(models.Model):
agent = models.ForeignKey( agent = models.ForeignKey(
@@ -31,8 +38,12 @@ class Alert(models.Model):
null=True, null=True,
blank=True, blank=True,
) )
alert_type = models.CharField(
max_length=20, choices=ALERT_TYPE_CHOICES, default="availability"
)
message = models.TextField(null=True, blank=True) message = models.TextField(null=True, blank=True)
alert_time = models.DateTimeField(auto_now_add=True, null=True, blank=True) alert_time = models.DateTimeField(auto_now_add=True, null=True, blank=True)
snoozed = models.BooleanField(default=False)
snooze_until = models.DateTimeField(null=True, blank=True) snooze_until = models.DateTimeField(null=True, blank=True)
resolved = models.BooleanField(default=False) resolved = models.BooleanField(default=False)
resolved_time = models.DateTimeField(null=True, blank=True) resolved_time = models.DateTimeField(null=True, blank=True)

View File

@@ -10,8 +10,8 @@ from .models import Alert, AlertTemplate
class AlertSerializer(ModelSerializer): class AlertSerializer(ModelSerializer):
hostname = ReadOnlyField(source="agent.hostname") hostname = ReadOnlyField(source="agent.hostname")
client = ReadOnlyField(source="agent.client") client = ReadOnlyField(source="agent.client.name")
site = ReadOnlyField(source="agent.site") site = ReadOnlyField(source="agent.site.name")
alert_time = DateTimeField(format="iso-8601") alert_time = DateTimeField(format="iso-8601")
class Meta: class Meta:

View File

@@ -1,8 +1,10 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.db.models import Q
from datetime import datetime as dt
from django.utils import timezone as djangotime
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import status
from .models import Alert, AlertTemplate from .models import Alert, AlertTemplate
@@ -10,12 +12,85 @@ from .serializers import AlertSerializer, AlertTemplateSerializer
class GetAddAlerts(APIView): class GetAddAlerts(APIView):
def get(self, request): def patch(self, request):
alerts = Alert.objects.all()
# Add time and severity filters # top 10 alerts for dashboard icon
if "top" in request.data.keys():
alerts = Alert.objects.filter(resolved=False, snooze_until=None).order_by(
"alert_time"
)[: int(request.data["top"])]
count = Alert.objects.filter(resolved=False).count()
return Response(
{
"alerts_count": count,
"alerts": AlertSerializer(alerts, many=True).data,
}
)
return Response(AlertSerializer(alerts, many=True).data) elif any(
key
in [
"timeFilter",
"clientFilter",
"severityFilter",
"resolvedFilter",
"snoozedFilter",
]
for key in request.data.keys()
):
clientFilter = Q()
severityFilter = Q()
timeFilter = Q()
resolvedFilter = Q()
snoozedFilter = Q()
if (
"snoozedFilter" in request.data.keys()
and not request.data["snoozedFilter"]
):
snoozedFilter = Q(snoozed=request.data["snoozedFilter"])
if (
"resolvedFilter" in request.data.keys()
and not request.data["resolvedFilter"]
):
resolvedFilter = Q(resolved=request.data["resolvedFilter"])
if "clientFilter" in request.data.keys():
from agents.models import Agent
from clients.models import Client
clients = Client.objects.filter(
pk__in=request.data["clientFilter"]
).values_list("id")
agents = Agent.objects.filter(site__client_id__in=clients).values_list(
"id"
)
clientFilter = Q(agent__in=agents)
if "severityFilter" in request.data.keys():
severityFilter = Q(severity__in=request.data["severityFilter"])
if "timeFilter" in request.data.keys():
timeFilter = Q(
alert_time__lte=djangotime.make_aware(dt.today()),
alert_time__gt=djangotime.make_aware(dt.today())
- djangotime.timedelta(days=int(request.data["timeFilter"])),
)
alerts = (
Alert.objects.filter(clientFilter)
.filter(severityFilter)
.filter(resolvedFilter)
.filter(snoozedFilter)
.filter(timeFilter)
)
return Response(AlertSerializer(alerts, many=True).data)
else:
alerts = Alert.objects.all()
return Response(AlertSerializer(alerts, many=True).data)
def post(self, request): def post(self, request):
serializer = AlertSerializer(data=request.data, partial=True) serializer = AlertSerializer(data=request.data, partial=True)

View File

@@ -1,18 +1,14 @@
<template> <template>
<q-btn dense flat icon="notifications"> <q-btn dense flat icon="notifications">
<q-badge v-if="alerts.length !== 0" color="red" floating transparent>{{ alertsLengthText() }}</q-badge> <q-badge v-if="alertsCount > 0" color="red" floating transparent>{{ alertsCountText() }}</q-badge>
<q-menu> <q-menu>
<q-list separator> <q-list separator>
<q-item v-if="alerts.length === 0">No New Alerts</q-item> <q-item v-if="alertsCount === 0">No New Alerts</q-item>
<q-item v-for="alert in alerts" :key="alert.id"> <q-item v-for="alert in topAlerts" :key="alert.id">
<q-item-section> <q-item-section>
<q-item-label overline>{{ alert.client }} - {{ alert.site }} - {{ alert.hostname }}</q-item-label> <q-item-label overline>{{ alert.client }} - {{ alert.site }} - {{ alert.hostname }}</q-item-label>
<q-item-label> <q-item-label>
<q-icon <q-icon size="xs" :class="`text-${alertIconColor(alert.severity)}`" :name="alert.severity"></q-icon>
size="xs"
:class="`text-${alertColor(alert.severity)}`"
:name="alert.severity"
></q-icon>
{{ alert.message }} {{ alert.message }}
</q-item-label> </q-item-label>
</q-item-section> </q-item-section>
@@ -20,51 +16,56 @@
<q-item-section side top> <q-item-section side top>
<q-item-label caption>{{ alertTime(alert.alert_time) }}</q-item-label> <q-item-label caption>{{ alertTime(alert.alert_time) }}</q-item-label>
<q-item-label> <q-item-label>
<q-icon name="snooze" size="xs"> <q-icon name="snooze" size="xs" class="cursor-pointer">
<q-tooltip>Snooze the alert for 24 hours</q-tooltip> <q-tooltip>Snooze the alert for 24 hours</q-tooltip>
</q-icon> </q-icon>
<q-icon name="alarm_off" size="xs"> <q-icon name="alarm_off" size="xs" class="cursor-pointer">
<q-tooltip>Dismiss alert</q-tooltip> <q-tooltip>Dismiss alert</q-tooltip>
</q-icon> </q-icon>
</q-item-label> </q-item-label>
</q-item-section> </q-item-section>
</q-item> </q-item>
<q-item clickable @click="showAlertsModal = true">View All Alerts ({{ alerts.length }})</q-item> <q-item clickable @click="showOverview">View All Alerts ({{ alertsCount }})</q-item>
</q-list> </q-list>
</q-menu> </q-menu>
<q-dialog
v-model="showAlertsModal"
maximized
transition-show="slide-up"
transition-hide="slide-down"
>
<AlertsOverview @close="showAlertsModal = false" />
</q-dialog>
</q-btn> </q-btn>
</template> </template>
<script> <script>
import { mapGetters } from "vuex";
import mixins from "@/mixins/mixins"; import mixins from "@/mixins/mixins";
import AlertsOverview from "@/components/modals/alerts/AlertsOverview"; import AlertsOverview from "@/components/modals/alerts/AlertsOverview";
export default { export default {
name: "AlertsIcon", name: "AlertsIcon",
components: { AlertsOverview },
mixins: [mixins], mixins: [mixins],
data() { data() {
return { return {
showAlertsModal: false, alertsCount: 0,
topAlerts: [],
}; };
}, },
methods: { methods: {
getAlerts() { getAlerts() {
this.$store.dispatch("alerts/getAlerts").catch(error => { this.$q.loading.show();
console.error(error); this.$axios
.patch("alerts/alerts/", { top: 10 })
.then(r => {
this.alertsCount = r.data.alerts_count;
this.topAlerts = r.data.alerts;
this.$q.loading.hide();
})
.catch(error => {
this.$q.loading.hide();
this.notifyError("Unable to get alerts");
});
},
showOverview() {
this.$q.dialog({
component: AlertsOverview,
parent: this,
}); });
}, },
alertColor(type) { alertIconColor(type) {
if (type === "error") { if (type === "error") {
return "red"; return "red";
} }
@@ -72,20 +73,14 @@ export default {
return "orange"; return "orange";
} }
}, },
alertsLengthText() { alertsCountText() {
if (this.alerts.length > 9) { if (this.alertsCount > 99) {
return "9+"; return "99+";
} else { } else {
return this.alerts.length; return this.alertsCount;
} }
}, },
}, },
computed: {
...mapGetters({
newAlerts: "alerts/getNewAlerts",
alerts: "alerts/getAlerts",
}),
},
mounted() { mounted() {
this.getAlerts(); this.getAlerts();
}, },

View File

@@ -1,5 +1,5 @@
<template> <template>
<div style="width: 60vw; max-width: 90vw"> <div style="width: 90vw; max-width: 90vw">
<q-card> <q-card>
<q-bar> <q-bar>
<q-btn @click="getScripts" class="q-mr-sm" dense flat push icon="refresh" />Script Manager <q-btn @click="getScripts" class="q-mr-sm" dense flat push icon="refresh" />Script Manager

View File

@@ -311,7 +311,7 @@ export default {
this.getPolicies(); this.getPolicies();
this.clearRow(); this.clearRow();
}, },
deletePolicy(id) { deletePolicy(policy) {
this.$q this.$q
.dialog({ .dialog({
title: "Delete policy?", title: "Delete policy?",
@@ -319,12 +319,16 @@ export default {
ok: { label: "Delete", color: "negative" }, ok: { label: "Delete", color: "negative" },
}) })
.onOk(() => { .onOk(() => {
this.$q.loading.show();
this.$axios this.$axios
.delete(`/automation/policies/${pk}/`) .delete(`/automation/policies/${policy.id}/`)
.then(r => { .then(r => {
this.refresh();
this.$q.loading.hide();
this.notifySuccess("Policy was deleted!"); this.notifySuccess("Policy was deleted!");
}) })
.catch(error => { .catch(error => {
this.$q.loading.hide();
this.notifyError("An Error occured while deleting policy"); this.notifyError("An Error occured while deleting policy");
}); });
}); });
@@ -422,6 +426,7 @@ export default {
this.$axios this.$axios
.put(`/automation/policies/${data.id}/`, data) .put(`/automation/policies/${data.id}/`, data)
.then(r => { .then(r => {
this.refresh();
this.$q.loading.hide(); this.$q.loading.hide();
this.notifySuccess(text); this.notifySuccess(text);
}) })

View File

@@ -16,8 +16,7 @@
:nodes="clientSiteTree" :nodes="clientSiteTree"
node-key="id" node-key="id"
selected-color="primary" selected-color="primary"
@update:selected="setSelectedPolicyId(key)" :selected.sync="selectedPolicyId"
default-expand-all
></q-tree> ></q-tree>
</div> </div>
</template> </template>
@@ -39,10 +38,10 @@
</q-tabs> </q-tabs>
<q-tab-panels v-model="selectedTab" animated transition-prev="jump-up" transition-next="jump-up"> <q-tab-panels v-model="selectedTab" animated transition-prev="jump-up" transition-next="jump-up">
<q-tab-panel name="checks"> <q-tab-panel name="checks">
<PolicyChecksTab :selectedPolicy="selectedPolicyId" /> <PolicyChecksTab v-if="!!selectedPolicyId" :selectedPolicy="selectedPolicyId" />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="tasks"> <q-tab-panel name="tasks">
<PolicyAutomatedTasksTab :selectedPolicy="selectedPolicyId" /> <PolicyAutomatedTasksTab v-if="!!selectedPolicyId" :selectedPolicy="selectedPolicyId" />
</q-tab-panel> </q-tab-panel>
</q-tab-panels> </q-tab-panels>
</template> </template>
@@ -85,12 +84,6 @@ export default {
this.notifyError("Error getting policy tree data"); this.notifyError("Error getting policy tree data");
}); });
}, },
setSelectedPolicyId(key) {
if (!key) {
return;
}
this.selectedPolicyId = this.$refs.tree.getNodeByKey(key);
},
processTreeDataFromApi(data) { processTreeDataFromApi(data) {
/* Structure /* Structure
* [{ * [{
@@ -114,10 +107,10 @@ export default {
// Used by tree for unique identification // Used by tree for unique identification
let unique_id = 0; let unique_id = 0;
for (let client in data) { for (let client of data) {
var client_temp = {}; var client_temp = {};
client_temp["label"] = data[client].name; client_temp["label"] = client.name;
client_temp["id"] = unique_id; client_temp["id"] = unique_id;
client_temp["icon"] = "business"; client_temp["icon"] = "business";
client_temp["selectable"] = false; client_temp["selectable"] = false;
@@ -126,41 +119,41 @@ export default {
unique_id--; unique_id--;
// Add any server policies assigned to client // Add any server policies assigned to client
if (data[client].server_policy !== null) { if (!!client.server_policy) {
let disabled = ""; let disabled = "";
// Indicate if the policy is active or not // Indicate if the policy is active or not
if (!data[client].server_policy.active) { if (!client.server_policy.active) {
disabled = " (disabled)"; disabled = " (disabled)";
} }
client_temp["children"].push({ client_temp["children"].push({
label: data[client].server_policy.name + " (Servers)" + disabled, label: client.server_policy.name + " (Servers)" + disabled,
icon: "policy", icon: "policy",
id: data[client].server_policy.id, id: client.server_policy.id,
}); });
} }
// Add any workstation policies assigned to client // Add any workstation policies assigned to client
if (data[client].workstation_policy !== null) { if (!!client.workstation_policy) {
let disabled = ""; let disabled = "";
// Indicate if the policy is active or not // Indicate if the policy is active or not
if (!data[client].workstation_policy.active) { if (!client.workstation_policy.active) {
disabled = " (disabled)"; disabled = " (disabled)";
} }
client_temp["children"].push({ client_temp["children"].push({
label: data[client].workstation_policy.name + " (Workstations)" + disabled, label: client.workstation_policy.name + " (Workstations)" + disabled,
icon: "policy", icon: "policy",
id: data[client].workstation_policy.id, id: client.workstation_policy.id,
}); });
} }
// Iterate through Sites // Iterate through Sites
for (let site in data[client].sites) { for (let site of client.sites) {
var site_temp = {}; var site_temp = {};
site_temp["label"] = data[client].sites[site].name; site_temp["label"] = site.name;
site_temp["id"] = unique_id; site_temp["id"] = unique_id;
site_temp["icon"] = "apartment"; site_temp["icon"] = "apartment";
site_temp["selectable"] = false; site_temp["selectable"] = false;
@@ -168,36 +161,36 @@ export default {
unique_id--; unique_id--;
// Add any server policies assigned to site // Add any server policies assigned to site
if (data[client].sites[site].server_policy !== null) { if (!!site.server_policy) {
site_temp["children"] = []; site_temp["children"] = [];
// Indicate if the policy is active or not // Indicate if the policy is active or not
let disabled = ""; let disabled = "";
if (!data[client].sites[site].server_policy.active) { if (!site.server_policy.active) {
disabled = " (disabled)"; disabled = " (disabled)";
} }
site_temp["children"].push({ site_temp["children"].push({
label: data[client].sites[site].server_policy.name + " (Servers)" + disabled, label: site.server_policy.name + " (Servers)" + disabled,
icon: "policy", icon: "policy",
id: data[client].sites[site].server_policy.id, id: site.server_policy.id,
}); });
} }
// Add any server policies assigned to site // Add any server policies assigned to site
if (data[client].sites[site].workstation_policy !== null) { if (!!site.workstation_policy) {
site_temp["children"] = []; site_temp["children"] = [];
// Indicate if the policy is active or not // Indicate if the policy is active or not
let disabled = ""; let disabled = "";
if (!data[client].sites[site].workstation_policy.active) { if (!site.workstation_policy.active) {
disabled = " (disabled)"; disabled = " (disabled)";
} }
site_temp["children"].push({ site_temp["children"].push({
label: data[client].sites[site].workstation_policy.name + " (Workstations)" + disabled, label: site.workstation_policy.name + " (Workstations)" + disabled,
icon: "policy", icon: "policy",
id: data[client].sites[site].workstation_policy.id, id: site.workstation_policy.id,
}); });
} }

View File

@@ -11,7 +11,7 @@
<q-form @submit="submit"> <q-form @submit="submit">
<q-card-section v-if="options.length > 0"> <q-card-section v-if="options.length > 0">
<q-select <q-select
v-if="type !== 'agent'" v-if="type === 'client' || type === 'site'"
class="q-mb-md" class="q-mb-md"
v-model="selectedServerPolicy" v-model="selectedServerPolicy"
:options="options" :options="options"
@@ -25,7 +25,7 @@
> >
</q-select> </q-select>
<q-select <q-select
v-if="type !== 'agent'" v-if="type === 'client' || type === 'site'"
v-model="selectedWorkstationPolicy" v-model="selectedWorkstationPolicy"
:options="options" :options="options"
outlined outlined
@@ -91,7 +91,7 @@ export default {
methods: { methods: {
submit() { submit() {
// check if data was changed // check if data was changed
if (this.type !== "agent") { if (this.type === "client" || this.type === "site") {
if ( if (
this.object.workstation_policy === this.selectedWorkstationPolicy && this.object.workstation_policy === this.selectedWorkstationPolicy &&
this.object.server_policy === this.selectedServerPolicy this.object.server_policy === this.selectedServerPolicy
@@ -99,11 +99,13 @@ export default {
this.hide(); this.hide();
return; return;
} }
} else { } else if (this.type === "agent") {
if (this.object.policy === this.selectedAgentPolicy) { if (this.object.policy === this.selectedAgentPolicy) {
this.hide(); this.hide();
return; return;
} }
} else {
return;
} }
this.$q.loading.show(); this.$q.loading.show();
@@ -112,14 +114,13 @@ export default {
type: this.type, type: this.type,
}; };
if (this.type !== "agent") { if (this.type === "client" || this.type === "site") {
data.server_policy = this.selectedServerPolicy; data.server_policy = this.selectedServerPolicy;
data.workstation_policy = this.selectedWorkstationPolicy; data.workstation_policy = this.selectedWorkstationPolicy;
} else { } else if (this.type === "agent") {
data.policy = this.selectedAgentPolicy; data.policy = this.selectedAgentPolicy;
} }
console.log(data);
this.$axios this.$axios
.post(`/automation/related/`, data) .post(`/automation/related/`, data)
.then(r => { .then(r => {

View File

@@ -1,86 +1,270 @@
<template> <template>
<q-card> <q-dialog ref="dialog" @hide="onHide" maximized transition-show="slide-up" transition-hide="slide-down">
<q-bar> <q-card>
Alerts Overview <q-bar>
<q-space /> <q-btn @click="search" class="q-mr-sm" dense flat push icon="refresh" />
<q-btn dense flat icon="close" v-close-popup> <q-space />
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip> Alerts Overview
</q-btn> <q-space />
</q-bar> <q-btn dense flat icon="close" v-close-popup>
<q-tooltip content-class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-separator /> <div class="text-h6 q-pl-sm q-pt-sm">Filter</div>
<div class="row">
<div class="q-pa-sm col-3">
<q-select
v-model="clientFilter"
:options="clientsOptions"
label="Clients"
multiple
outlined
dense
use-chips
map-options
emit-value
/>
</div>
<div class="q-pa-sm col-3">
<q-select
v-model="severityFilter"
:options="severityOptions"
label="Severity"
multiple
outlined
dense
use-chips
map-options
emit-value
/>
</div>
<div class="q-pa-sm col-2">
<q-select outlined dense v-model="timeFilter" label="Time" emit-value map-options :options="timeOptions" />
</div>
<div class="q-pa-sm col-2">
<q-checkbox outlined dense v-model="includeSnoozed" label="Include snoozed" />
<q-checkbox outlined dense v-model="includeResolved" label="Include resolved" />
</div>
<div class="q-pa-sm col-1">
<q-btn color="primary" label="Search" @click="search" />
</div>
</div>
<q-card-section class="row"> <q-separator />
<div class="col-3">
<q-input outlined dense v-model="search"> <q-card-section>
<template v-slot:append> <q-table
<q-icon v-if="search !== ''" name="close" @click="search = ''" class="cursor-pointer" /> :table-class="{ 'table-bgcolor': !$q.dark.isActive, 'table-bgcolor-dark': $q.dark.isActive }"
<q-icon name="search" /> class="tabs-tbl-sticky"
:data="alerts"
:columns="columns"
:rows-per-page-options="[0]"
:pagination.sync="pagination"
:no-data-label="noDataText"
:visible-columns="visibleColumns"
:selected.sync="selectedAlerts"
selection="multiple"
binary-state-sort
row-key="id"
dense
virtual-scroll
>
<template v-slot:top="props">
<div class="col-2 q-table__title">Treats</div>
<q-btn-dropdown label="Bulk Actions" v-if="props.selected > 0">
<q-list dense>
<q-item clickable v-close-popup @click="snoozeAlert(props.selected)">
<q-item-section>
<q-item-label>Snooze alerts</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="resolveAlert(props.selected)">
<q-item-section>
<q-item-label>Resolve alert</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</template> </template>
<template v-slot:hint>Type in client, site, or agent name</template> <template v-slot:body-cell-actions="props">
</q-input> <q-td :props="props">
</div> <q-icon name="snooze" size="sm" class="cursor-pointer" @click="snoozeAlert(props.row)">
<q-tooltip>Snooze alert</q-tooltip>
</q-icon>
<q-icon name="alarm_off" size="sm" class="cursor-pointer" @click="resolveAlert(props.row)">
<q-tooltip>Dismiss alert</q-tooltip>
</q-icon>
</q-td>
</template>
<div class="col-3"> <template v-slot:body-cell-severity="props">
<q-checkbox outlined dense v-model="includeDismissed" label="Include dismissed alerts?" /> <q-td :props="props">
</div> <q-badge :color="alertColor(props.row.severity)">{{ capitalize(props.row.severity) }}</q-badge>
</q-card-section> </q-td>
</template>
<q-separator /> </q-table>
</q-card-section>
<q-list separator> </q-card>
<q-item v-if="alerts.length === 0">No Alerts!</q-item> </q-dialog>
<q-item v-for="alert in alerts" :key="alert.id">
<q-item-section>
<q-item-label overline>{{ alert.client }} - {{ alert.site }} - {{ alert.hostname }}</q-item-label>
<q-item-label>
<q-icon size="sm" :class="`text-${alertColor(alert.severity)}`" :name="alert.severity"></q-icon>
{{ alert.message }}
</q-item-label>
</q-item-section>
<q-item-section side top>
<q-item-label caption>{{ alertTime(alert.alert_time) }}</q-item-label>
<q-item-label>
<q-icon name="snooze" size="sm">
<q-tooltip>Snooze the alert for 24 hours</q-tooltip>
</q-icon>
<q-icon name="alarm_off" size="sm">
<q-tooltip>Dismiss alert</q-tooltip>
</q-icon>
</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-card>
</template> </template>
<script> <script>
import mixins from "@/mixins/mixins"; import mixins from "@/mixins/mixins";
import { mapGetters } from "vuex";
export default { export default {
name: "AlertsOverview", name: "AlertsOverview",
mixins: [mixins], mixins: [mixins],
data() { data() {
return { return {
search: "", alerts: [],
includeDismissed: false, selectedAlerts: [],
severityFilter: [],
clientFilter: [],
timeFilter: 30,
includeResolved: false,
includeSnoozed: false,
searched: false,
clientsOptions: [],
severityOptions: [
{ label: "Informational", value: "info" },
{ label: "Warning", value: "warning" },
{ label: "Error", value: "error" },
],
timeOptions: [
{ value: 1, label: "1 Day Ago" },
{ value: 7, label: "1 Week Ago" },
{ value: 30, label: "30 Days Ago" },
{ value: 90, label: "3 Months Ago" },
{ value: 180, label: "6 Months Ago" },
{ value: 365, label: "1 Year Ago" },
{ value: 0, label: "Everything" },
],
columns: [
{ name: "alert_time", label: "Time", field: "alert_time", align: "left", sortable: true },
{ name: "hostname", label: "Agent", field: "hostname", align: "left", sortable: true },
{
name: "alert_type",
label: "Type",
field: "alert_type",
align: "left",
sortable: true,
format: a => this.capitalize(a),
},
{ name: "severity", label: "Severity", field: "severity", align: "left", sortable: true },
{ name: "message", label: "Message", field: "message", align: "left", sortable: true },
{ name: "resolved_on", label: "Resolved On", field: "resolved_on", align: "left", sortable: true },
{ name: "snooze_until", label: "Snoozed Until", field: "snoozed_until", align: "left", sortable: true },
{ name: "actions", label: "Actions", align: "left" },
],
pagination: {
rowsPerPage: 0,
sortBy: "alert_time",
descending: true,
},
}; };
}, },
computed: {
noDataText() {
return this.searched ? "No data found. Try to refine you search" : "Click search to find alerts";
},
visibleColumns() {
return this.columns.map(column => {
if (column.name === "snooze_until") {
if (this.includeSnoozed) return column.name;
} else if (column.name === "resolved_on") {
if (this.includeResolved) return column.name;
} else {
return column.name;
}
});
},
},
methods: { methods: {
getAlerts() { getAlerts() {
this.$q.loading.show(); this.$q.loading.show();
this.$store this.$axios
.dispatch("alerts/getAlerts") .get("alerts/alerts/")
.then(response => { .then(r => {
this.alerts = r.data;
this.$q.loading.hide(); this.$q.loading.hide();
}) })
.catch(error => { .catch(error => {
this.$q.loading.hide(); this.$q.loading.hide();
this.notifyError("Something went wrong"); this.notifyError("There was an issue getting alerts");
});
},
getClients() {
this.$axios.get("/clients/clients/").then(r => {
this.clientsOptions = Object.freeze(r.data.map(client => ({ label: client.name, value: client.id })));
});
},
search() {
this.$q.loading.show();
this.searched = true;
let data = {
snoozedFilter: this.includeSnoozed,
resolvedFilter: this.includeResolved,
};
if (this.clientFilter.length > 0) data["clientFilter"] = this.clientFilter;
if (this.timeFilter) data["timeFilter"] = this.timeFilter;
if (this.severityFilter.length > 0) data["severityFilter"] = this.severityFilter;
this.$axios
.patch("/alerts/alerts/", data)
.then(r => {
this.$q.loading.hide();
this.alerts = Object.freeze(r.data);
})
.catch(e => {
this.notifyError("There was an issue getting alerts");
this.$q.loading.hide();
});
},
snoozeAlert(alert) {
this.$q.loading.show();
const data = {
id: alert.id,
snoozed: true,
};
this.$axios
.put(`alerts/alerts/${alert.id}/`, data)
.then(r => {
this.search();
this.$q.loading.hide();
this.notifySuccess("The alert has been snoozed for 48 hours");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an issue snoozing alert");
});
},
resolveAlert(alert) {
this.$q.loading.show();
const data = {
id: alert.id,
resolved: true,
};
this.$axios
.put(`alerts/alerts/${alert.id}/`, data)
.then(r => {
this.search();
this.$q.loading.hide();
this.notifySuccess("The alert has been snoozed for 48 hours");
})
.catch(e => {
this.$q.loading.hide();
this.notifyError("There was an issue snoozing alert");
}); });
}, },
alertColor(severity) { alertColor(severity) {
@@ -94,14 +278,18 @@ export default {
return "blue"; return "blue";
} }
}, },
}, show() {
computed: { this.$refs.dialog.show();
...mapGetters({ },
alerts: "alerts/getAlerts", hide() {
}), this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
}, },
mounted() { mounted() {
this.getAlerts(); this.getClients();
}, },
}; };
</script> </script>

View File

@@ -69,7 +69,7 @@
</q-menu> </q-menu>
</q-chip> </q-chip>
<!--<AlertsIcon />--> <AlertsIcon />
<q-btn-dropdown flat no-caps stretch :label="user"> <q-btn-dropdown flat no-caps stretch :label="user">
<q-list> <q-list>