Files
tacticalrmm-web/src/components/modals/alerts/AlertTemplateForm.vue
2022-12-01 00:44:56 +00:00

902 lines
29 KiB
Vue

<template>
<q-dialog ref="dialog" @hide="onHide">
<q-card style="width: 90vw; max-width: 90vw">
<q-bar>
{{ title }}
<q-space />
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip class="bg-white text-primary">Close</q-tooltip>
</q-btn>
</q-bar>
<q-stepper
v-model="step"
ref="stepper"
alternative-labels
header-nav
color="primary"
animated
>
<q-step
:name="1"
:error="!template.name && step > 1"
title="General Settings"
icon="settings"
>
<q-card flat>
<q-card-section>
<q-input
label="Name"
class="q-mb-none"
outlined
dense
v-model="template.name"
:rules="[(val) => !!val || '*Required']"
/>
</q-card-section>
<q-card-section>
<q-toggle
v-model="template.is_active"
color="green"
label="Enabled"
left-label
/>
</q-card-section>
<div class="q-pl-md text-subtitle1">
Email Settings (Overrides global email settings)
</div>
<q-card-section>
<q-input
label="Email From address"
class="q-mb-sm"
outlined
dense
v-model="template.email_from"
/>
</q-card-section>
<q-card-section class="row">
<div class="col-2 q-mb-sm">Email recipients</div>
<div class="col-4 q-mb-sm">
<q-list dense v-if="template.email_recipients.length !== 0">
<q-item
v-for="email in template.email_recipients"
:key="email"
dense
>
<q-item-section>
<q-item-label>{{ email }}</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon
class="cursor-pointer"
name="delete"
color="red"
@click="removeEmail(email)"
/>
</q-item-section>
</q-item>
</q-list>
<q-list v-else>
<q-item-section>
<q-item-label>No recipients</q-item-label>
</q-item-section>
</q-list>
</div>
<div class="col-3 q-mb-sm"></div>
<div class="col-3 q-mb-sm">
<q-btn
size="sm"
icon="fas fa-plus"
color="secondary"
label="Add email"
@click="toggleAddEmail"
/>
</div>
</q-card-section>
<div class="q-pl-md text-subtitle1">
SMS Settings (Overrides global SMS settings)
</div>
<q-card-section class="row">
<div class="col-2 q-mb-sm">SMS recipients</div>
<div class="col-4 q-mb-md">
<q-list dense v-if="template.text_recipients.length !== 0">
<q-item
v-for="num in template.text_recipients"
:key="num"
dense
>
<q-item-section>
<q-item-label>{{ num }}</q-item-label>
</q-item-section>
<q-item-section side>
<q-icon
class="cursor-pointer"
name="delete"
color="red"
@click="removeSMSNumber(num)"
/>
</q-item-section>
</q-item>
</q-list>
<q-list v-else>
<q-item-section>
<q-item-label>No recipients</q-item-label>
</q-item-section>
</q-list>
</div>
<div class="col-3 q-mb-sm"></div>
<div class="col-3 q-mb-sm">
<q-btn
class="cursor-pointer"
size="sm"
icon="fas fa-plus"
color="secondary"
label="Add sms number"
@click="toggleAddSMSNumber"
/>
</div>
</q-card-section>
</q-card>
</q-step>
<q-step :name="2" title="Alert Actions" icon="warning">
<q-card flat>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Failure Settings
<q-tooltip>
The selected script will run when an alert is triggered. This
script will run on any online agent.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-select
class="q-mb-sm"
label="Failure action"
dense
options-dense
outlined
clearable
v-model="template.action"
:options="scriptOptions"
map-options
emit-value
@update:model-value="setScriptDefaults('failure')"
>
<template v-slot:option="scope">
<q-item
v-if="!scope.opt.category"
v-bind="scope.itemProps"
class="q-pl-lg"
>
<q-item-section>
<q-item-label v-html="scope.opt.label"></q-item-label>
</q-item-section>
</q-item>
<q-item-label
v-if="scope.opt.category"
v-bind="scope.itemProps"
header
class="q-pa-sm"
>{{ scope.opt.category }}</q-item-label
>
</template>
</q-select>
<q-select
class="q-mb-sm"
dense
label="Failure action arguments (press Enter after typing each argument)"
filled
v-model="template.action_args"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-select
class="q-mb-sm"
dense
label="Failure action environment vars (press Enter after typing each key=value pair)"
filled
v-model="template.action_env_vars"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-input
class="q-mb-sm"
label="Failure action timeout (seconds)"
outlined
type="number"
v-model.number="template.action_timeout"
dense
:rules="[
(val) => !!val || 'Failure action timeout is required',
(val) => val > 0 || 'Timeout must be greater than 0',
(val) => val <= 60 || 'Timeout must be 60 or less',
]"
/>
</q-card-section>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Resolved Settings
<q-tooltip>
The selected script will run when an alert is resolved. This
script will run on any online agent.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-select
class="q-mb-sm"
label="Resolved Action"
dense
options-dense
outlined
clearable
v-model="template.resolved_action"
:options="scriptOptions"
map-options
emit-value
@update:model-value="setScriptDefaults('resolved')"
>
<template v-slot:option="scope">
<q-item
v-if="!scope.opt.category"
v-bind="scope.itemProps"
class="q-pl-lg"
>
<q-item-section>
<q-item-label v-html="scope.opt.label"></q-item-label>
</q-item-section>
</q-item>
<q-item-label
v-if="scope.opt.category"
v-bind="scope.itemProps"
header
class="q-pa-sm"
>{{ scope.opt.category }}</q-item-label
>
</template>
</q-select>
<q-select
class="q-mb-sm"
dense
label="Resolved action arguments (press Enter after typing each argument)"
filled
v-model="template.resolved_action_args"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-select
class="q-mb-sm"
dense
label="Resolved action environment vars (press Enter after typing each key=value pair)"
filled
v-model="template.resolved_action_env_vars"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
<q-input
class="q-mb-sm"
label="Resolved action timeout (seconds)"
outlined
type="number"
v-model.number="template.resolved_action_timeout"
dense
:rules="[
(val) => !!val || 'Resolved action timeout is required',
(val) => val > 0 || 'Timeout must be greater than 0',
(val) => val <= 60 || 'Timeout must be 60 or less',
]"
/>
</q-card-section>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Run actions only on
<q-tooltip>
The selected script will only run on the following types of
alerts
</q-tooltip>
</span>
</div>
<q-card-section>
<q-toggle
v-model="template.agent_script_actions"
label="Agents"
color="green"
left-label
/>
<q-toggle
v-model="template.check_script_actions"
label="Checks"
color="green"
left-label
/>
<q-toggle
v-model="template.task_script_actions"
label="Tasks"
color="green"
left-label
/>
</q-card-section>
</q-card>
</q-step>
<q-step :name="3" title="Agent Overdue Settings" icon="devices">
<q-card flat>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Failure Settings
<q-tooltip>
Select what notifications should be sent when an agent is
overdue. Enabled will override the agent notification settings
and always notify. Not configured will use what notification
settings are configured on the agent. Disabled will override
the agent notification settings and never notify.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-toggle
v-model="template.agent_always_email"
label="Email"
color="green"
left-label
toggle-indeterminate
/>
<q-toggle
v-model="template.agent_always_text"
label="Text"
color="green"
left-label
toggle-indeterminate
/>
<q-toggle
v-model="template.agent_always_alert"
label="Dashboard"
color="green"
left-label
toggle-indeterminate
/>
</q-card-section>
<q-card-section>
<q-input
label="Alert again if not resolved after (days)"
outlined
type="number"
v-model.number="template.agent_periodic_alert_days"
dense
:rules="[
(val) => val >= 0 || 'Periodic days must be 0 or greater',
]"
/>
</q-card-section>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Resolved Settings
<q-tooltip>
Select what notifications should be sent when an overdue agent
is back online.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-toggle
v-model="template.agent_email_on_resolved"
label="Email"
color="green"
left-label
/>
<q-toggle
v-model="template.agent_text_on_resolved"
label="Text"
color="green"
left-label
/>
</q-card-section>
</q-card>
</q-step>
<q-step :name="4" title="Check Settings" icon="fas fa-check-double">
<q-card flat>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Failure Settings
<q-tooltip>
Select what notifications are sent when a check fails. Enabled
will override the check notification settings and always
notify. Not configured will use the notification settings
configured on the check. Disabled will override the check
notification settings and never notify.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-toggle
v-model="template.check_always_email"
label="Email"
color="green"
left-label
toggle-indeterminate
/>
<q-toggle
v-model="template.check_always_text"
label="Text"
color="green"
left-label
toggle-indeterminate
/>
<q-toggle
v-model="template.check_always_alert"
label="Dashboard"
color="green"
left-label
toggle-indeterminate
/>
</q-card-section>
<q-card-section>
<q-select
label="Only email on alert severity"
hint="Defaults to 'error' and 'warning'"
v-model="template.check_email_alert_severity"
outlined
dense
options-dense
multiple
use-chips
emit-value
map-options
:options="severityOptions"
/>
</q-card-section>
<q-card-section>
<q-select
label="Only text on alert severity"
hint="Defaults to 'error' and 'warning'"
v-model="template.check_text_alert_severity"
outlined
dense
options-dense
multiple
use-chips
emit-value
map-options
:options="severityOptions"
/>
</q-card-section>
<q-card-section>
<q-select
label="Only show dashboard alert on severity"
hint="Defaults to 'error', 'warning', and 'info'"
v-model="template.check_dashboard_alert_severity"
outlined
dense
options-dense
multiple
use-chips
emit-value
map-options
:options="severityOptions"
/>
</q-card-section>
<q-card-section>
<q-input
label="Alert again if not resolved after (days)"
outlined
type="number"
v-model.number="template.check_periodic_alert_days"
dense
:rules="[
(val) => val >= 0 || 'Periodic days must be 0 or greater',
]"
/>
</q-card-section>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Resolved Settings
<q-tooltip>
Select what notifications are sent when a failed check is
resolved.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-toggle
v-model="template.check_email_on_resolved"
label="Email"
color="green"
left-label
/>
<q-toggle
v-model="template.check_text_on_resolved"
label="Text"
color="green"
left-label
/>
</q-card-section>
</q-card>
</q-step>
<q-step :name="5" title="Automated Task Settings" icon="fas fa-tasks">
<q-card flat>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Failure Settings
<q-tooltip>
Select what notifications are sent when an automated task
fails. Enabled will override the task notification settings
and always notify. Not configured will use the notification
settings configured on the task. Disabled will override the
task notification settings and never notify.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-toggle
v-model="template.task_always_email"
label="Email"
color="green"
left-label
toggle-indeterminate
/>
<q-toggle
v-model="template.task_always_text"
label="Text"
color="green"
left-label
toggle-indeterminate
/>
<q-toggle
v-model="template.task_always_alert"
label="Dashboard"
color="green"
left-label
toggle-indeterminate
/>
</q-card-section>
<q-card-section>
<q-select
label="Only email on alert severity"
hint="Defaults to 'error' and 'warning'"
v-model="template.task_email_alert_severity"
outlined
dense
options-dense
multiple
use-chips
emit-value
map-options
:options="severityOptions"
/>
</q-card-section>
<q-card-section>
<q-select
label="Only text on alert severity"
hint="Defaults to 'error' and 'warning'"
v-model="template.task_text_alert_severity"
outlined
dense
options-dense
multiple
use-chips
emit-value
map-options
:options="severityOptions"
/>
</q-card-section>
<q-card-section>
<q-select
label="Only show dashboard alert on severity"
hint="Defaults to 'error', 'warning', and 'info'"
v-model="template.task_dashboard_alert_severity"
outlined
dense
options-dense
multiple
use-chips
emit-value
map-options
:options="severityOptions"
/>
</q-card-section>
<q-card-section>
<q-input
label="Alert again if not resolved (days)"
outlined
type="number"
v-model.number="template.task_periodic_alert_days"
dense
:rules="[
(val) => val >= 0 || 'Periodic days must be 0 or greater',
]"
/>
</q-card-section>
<div class="q-pl-md text-subtitle1">
<span style="text-decoration: underline; cursor: help"
>Alert Resolved Settings
<q-tooltip>
Select what notifications are sent when a failed task is
resolved.
</q-tooltip>
</span>
</div>
<q-card-section>
<q-toggle
v-model="template.task_email_on_resolved"
label="Email"
color="green"
left-label
/>
<q-toggle
v-model="template.check_text_on_resolved"
label="Text"
color="green"
left-label
/>
</q-card-section>
</q-card>
</q-step>
<template v-slot:navigation>
<q-stepper-navigation class="row">
<q-btn
v-if="step > 1"
flat
color="primary"
@click="$refs.stepper.previous()"
label="Back"
class="q-mr-xs"
/>
<q-btn
v-if="step < 5"
@click="$refs.stepper.next()"
color="primary"
label="Next"
/>
<q-space />
<q-btn @click="onSubmit" color="primary" label="Submit" />
</q-stepper-navigation>
</template>
</q-stepper>
</q-card>
</q-dialog>
</template>
<script>
import mixins from "@/mixins/mixins";
import { mapGetters } from "vuex";
export default {
name: "AlertTemplateForm",
emits: ["hide", "ok", "cancel"],
mixins: [mixins],
props: { alertTemplate: Object },
data() {
return {
step: 1,
template: {
name: "",
is_active: true,
action: null,
action_args: [],
action_env_vars: [],
action_timeout: 15,
resolved_action: null,
resolved_action_args: [],
resolved_action_env_vars: [],
resolved_action_timeout: 15,
email_recipients: [],
email_from: "",
text_recipients: [],
agent_email_on_resolved: false,
agent_text_on_resolved: false,
agent_always_email: null,
agent_always_text: null,
agent_always_alert: null,
agent_periodic_alert_days: 0,
agent_script_actions: true,
check_email_alert_severity: [],
check_text_alert_severity: [],
check_dashboard_alert_severity: [],
check_email_on_resolved: false,
check_text_on_resolved: false,
check_always_email: null,
check_always_text: null,
check_always_alert: null,
check_periodic_alert_days: 0,
check_script_actions: true,
task_email_alert_severity: [],
task_text_alert_severity: [],
task_dashboard_alert_severity: [],
task_email_on_resolved: false,
task_text_on_resolved: false,
task_always_email: null,
task_always_text: null,
task_always_alert: null,
task_periodic_alert_days: 0,
task_script_actions: true,
},
scriptOptions: [],
severityOptions: [
{ label: "Error", value: "error" },
{ label: "Warning", value: "warning" },
{ label: "Informational", value: "info" },
],
thumbStyle: {
right: "2px",
borderRadius: "5px",
backgroundColor: "#027be3",
width: "5px",
opacity: 0.75,
},
};
},
computed: {
...mapGetters(["showCommunityScripts"]),
title() {
return this.editing ? "Edit Alert Template" : "Add Alert Template";
},
editing() {
return !!this.alertTemplate;
},
},
methods: {
setScriptDefaults(type) {
if (type === "failure") {
const script = this.scriptOptions.find(
(i) => i.value === this.template.action
);
this.template.action_args = script.args;
this.template.action_env_vars = script.env_vars;
} else if (type === "resolved") {
const script = this.scriptOptions.find(
(i) => i.value === this.template.resolved_action
);
this.template.resolved_action_args = script.args;
this.template.resolved_action_env_vars = script.env_vars;
}
},
toggleAddEmail() {
this.$q
.dialog({
title: "Add email",
prompt: {
model: "",
isValid: (val) => this.isValidEmail(val),
type: "email",
},
cancel: true,
ok: { label: "Add", color: "primary" },
persistent: false,
})
.onOk((data) => {
this.template.email_recipients.push(data);
});
},
toggleAddSMSNumber() {
this.$q
.dialog({
title: "Add number",
message:
"Use E.164 format: must have the <b>+</b> symbol and <span class='text-red'>country code</span>, followed by the <span class='text-green'>phone number</span> e.g. <b>+<span class='text-red'>1</span><span class='text-green'>2131231234</span></b>",
prompt: {
model: "",
},
html: true,
cancel: true,
ok: { label: "Add", color: "primary" },
persistent: false,
})
.onOk((data) => {
this.template.text_recipients.push(data);
});
},
removeEmail(email) {
const removed = this.template.email_recipients.filter((k) => k !== email);
this.template.email_recipients = removed;
},
removeSMSNumber(num) {
const removed = this.template.text_recipients.filter((k) => k !== num);
this.template.text_recipients = removed;
},
onSubmit() {
if (!this.template.name) {
this.notifyError("Name needs to be set");
return;
}
this.$q.loading.show();
if (this.editing) {
this.$axios
.put(`alerts/templates/${this.template.id}/`, this.template)
.then(() => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Alert Template edited!");
})
.catch(() => {
this.$q.loading.hide();
});
} else {
this.$axios
.post("alerts/templates/", this.template)
.then(() => {
this.$q.loading.hide();
this.onOk();
this.notifySuccess("Alert Template was added!");
})
.catch(() => {
this.$q.loading.hide();
});
}
},
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onHide() {
this.$emit("hide");
},
onOk() {
this.$emit("ok");
this.hide();
},
},
mounted() {
this.getScriptOptions(this.showCommunityScripts).then(
(options) => (this.scriptOptions = Object.freeze(options))
);
// Copy alertTemplate prop locally
if (this.editing) Object.assign(this.template, this.alertTemplate);
},
};
</script>