mirror of
https://github.com/hay-kot/mealie.git
synced 2025-08-22 14:33:33 -07:00
complete notification feature
This commit is contained in:
parent
c4d1a9ff9a
commit
0092d07c6e
11 changed files with 318 additions and 243 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { baseURL } from "./api-utils";
|
import { baseURL } from "./api-utils";
|
||||||
import { apiReq } from "./api-utils";
|
import { apiReq } from "./api-utils";
|
||||||
|
import i18n from "@/i18n.js";
|
||||||
|
|
||||||
const prefix = baseURL + "about";
|
const prefix = baseURL + "about";
|
||||||
|
|
||||||
|
@ -47,11 +48,21 @@ export const aboutAPI = {
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async testNotificationByID(id) {
|
async testNotificationByID(id) {
|
||||||
const response = await apiReq.post(aboutURLs.testNotifications, { id: id });
|
const response = await apiReq.post(
|
||||||
|
aboutURLs.testNotifications,
|
||||||
|
{ id: id },
|
||||||
|
() => i18n.t("events.something-went-wrong"),
|
||||||
|
() => i18n.t("events.test-message-sent")
|
||||||
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
async testNotificationByURL(url) {
|
async testNotificationByURL(url) {
|
||||||
const response = await apiReq.post(aboutURLs.testNotifications, { test_url: url });
|
const response = await apiReq.post(
|
||||||
|
aboutURLs.testNotifications,
|
||||||
|
{ test_url: url },
|
||||||
|
() => i18n.t("events.something-went-wrong"),
|
||||||
|
() => i18n.t("events.test-message-sent")
|
||||||
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
// async getAppInfo() {
|
// async getAppInfo() {
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const userAPI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
delete(id) {
|
delete(id) {
|
||||||
return apiReq.delete(usersURLs.userID(id), null, deleteErrorText, function() {
|
return apiReq.delete(usersURLs.userID(id), null, deleteErrorText, () => {
|
||||||
return i18n.t("user.user-deleted");
|
return i18n.t("user.user-deleted");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<slot name="open" v-bind="{ open }"> </slot>
|
<slot name="open" v-bind="{ open }"> </slot>
|
||||||
<v-dialog v-model="dialog" :width="modalWidth + 'px'" :content-class="top ? 'top-dialog' : undefined">
|
<v-dialog v-model="dialog" :width="modalWidth + 'px'" :content-class="top ? 'top-dialog' : undefined">
|
||||||
<v-card class="pb-10" height="100%">
|
<v-card height="100%">
|
||||||
<v-app-bar dark :color="color" class="mt-n1 mb-0">
|
<v-app-bar dark :color="color" class="mt-n1 mb-0">
|
||||||
<v-icon large left>
|
<v-icon large left>
|
||||||
{{ titleIcon }}
|
{{ titleIcon }}
|
||||||
|
@ -11,7 +11,9 @@
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-progress-linear class="mt-1" v-if="loading" indeterminate color="primary"></v-progress-linear>
|
<v-progress-linear class="mt-1" v-if="loading" indeterminate color="primary"></v-progress-linear>
|
||||||
<slot> </slot>
|
|
||||||
|
<slot v-bind="{ submitEvent }"> </slot>
|
||||||
|
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<slot name="card-actions">
|
<slot name="card-actions">
|
||||||
<v-btn text color="grey" @click="dialog = false">
|
<v-btn text color="grey" @click="dialog = false">
|
||||||
|
@ -22,13 +24,15 @@
|
||||||
<v-btn color="error" text @click="deleteEvent" v-if="$listeners.delete">
|
<v-btn color="error" text @click="deleteEvent" v-if="$listeners.delete">
|
||||||
{{ $t("general.delete") }}
|
{{ $t("general.delete") }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
<v-btn color="success" @click="submitEvent">
|
<v-btn color="success" type="submit" @click="submitEvent">
|
||||||
{{ submitText }}
|
{{ submitText }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</slot>
|
</slot>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
|
|
||||||
<slot name="below-actions"> </slot>
|
<div class="pb-4" v-if="$slots['below-actions']">
|
||||||
|
<slot name="below-actions"> </slot>
|
||||||
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,6 +63,9 @@ export default {
|
||||||
submitText: {
|
submitText: {
|
||||||
default: () => i18n.t("general.create"),
|
default: () => i18n.t("general.create"),
|
||||||
},
|
},
|
||||||
|
keepOpen: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -68,7 +75,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
determineClose() {
|
determineClose() {
|
||||||
return this.submitted && !this.loading;
|
return this.submitted && !this.loading && !this.keepOpen;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -82,6 +89,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
submitEvent() {
|
submitEvent() {
|
||||||
|
console.log("Submit");
|
||||||
this.$emit("submit");
|
this.$emit("submit");
|
||||||
this.submitted = true;
|
this.submitted = true;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="text-center ma-2">
|
<div class="text-center ma-2">
|
||||||
<v-snackbar v-model="snackbar.open" top :color="snackbar.color" timeout="3500">
|
<v-snackbar v-model="snackbar.open" top :color="snackbar.color" timeout="3500">
|
||||||
|
<v-icon dark left>
|
||||||
|
{{ icon }}
|
||||||
|
</v-icon>
|
||||||
|
|
||||||
{{ snackbar.title }}
|
{{ snackbar.title }}
|
||||||
{{ snackbar.text }}
|
{{ snackbar.text }}
|
||||||
|
|
||||||
|
@ -25,6 +29,18 @@ export default {
|
||||||
return this.$store.getters.getSnackbar;
|
return this.$store.getters.getSnackbar;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
icon() {
|
||||||
|
switch (this.snackbar.color) {
|
||||||
|
case "error":
|
||||||
|
return "mdi-alert";
|
||||||
|
case "success":
|
||||||
|
return "mdi-checkbox-marked-circle";
|
||||||
|
case "info":
|
||||||
|
return "mdi-information";
|
||||||
|
default:
|
||||||
|
return "mdi-bell-alert";
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
|
@ -31,6 +31,16 @@
|
||||||
"category-updated": "Category updated",
|
"category-updated": "Category updated",
|
||||||
"uncategorized-count": "Uncategorized {count}"
|
"uncategorized-count": "Uncategorized {count}"
|
||||||
},
|
},
|
||||||
|
"events": {
|
||||||
|
"notification": "Notification",
|
||||||
|
"apprise-url": "Apprise URL",
|
||||||
|
"subscribed-events": "Subscribed Events",
|
||||||
|
"scheduled": "Scheduled",
|
||||||
|
"database": "Database",
|
||||||
|
"test-message-sent": "Test Message Sent",
|
||||||
|
"something-went-wrong": "Something Went Wrong!",
|
||||||
|
"new-notification-form-description": "Mealie uses the Apprise library to generate notifications. They offer many options for services to use for notifications. Refer to their wiki for a comprehensive guide on how to create the URL for your service. If available, selecting the type of your notification may include extra features."
|
||||||
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"apply": "Apply",
|
"apply": "Apply",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
@ -55,6 +65,7 @@
|
||||||
"file-uploaded": "File uploaded",
|
"file-uploaded": "File uploaded",
|
||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"friday": "Friday",
|
"friday": "Friday",
|
||||||
|
"general": "General",
|
||||||
"get": "Get",
|
"get": "Get",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"image-upload-failed": "Image upload failed",
|
"image-upload-failed": "Image upload failed",
|
||||||
|
@ -70,6 +81,7 @@
|
||||||
"random": "Random",
|
"random": "Random",
|
||||||
"rating": "Rating",
|
"rating": "Rating",
|
||||||
"recent": "Recent",
|
"recent": "Recent",
|
||||||
|
"recipe": "Recipe",
|
||||||
"recipes": "Recipes",
|
"recipes": "Recipes",
|
||||||
"rename-object": "Rename {0}",
|
"rename-object": "Rename {0}",
|
||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
|
@ -88,6 +100,8 @@
|
||||||
"thursday": "Thursday",
|
"thursday": "Thursday",
|
||||||
"token": "Token",
|
"token": "Token",
|
||||||
"tuesday": "Tuesday",
|
"tuesday": "Tuesday",
|
||||||
|
"type": "Type",
|
||||||
|
"test": "Test",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"updated": "Updated",
|
"updated": "Updated",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<base-dialog
|
<BaseDialog
|
||||||
ref="assignDialog"
|
ref="assignDialog"
|
||||||
title-icon="mdi-tag"
|
title-icon="mdi-tag"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
:single-column="true"
|
:single-column="true"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</base-dialog>
|
</BaseDialog>
|
||||||
|
|
||||||
<v-btn @click="openDialog" small color="success">
|
<v-btn @click="openDialog" small color="success">
|
||||||
{{ $t("settings.toolbox.bulk-assign") }}
|
{{ $t("settings.toolbox.bulk-assign") }}
|
||||||
|
|
236
frontend/src/pages/Admin/ToolBox/EventNotification.vue
Normal file
236
frontend/src/pages/Admin/ToolBox/EventNotification.vue
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-card outlined class="mt-n1">
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<BaseDialog
|
||||||
|
:keep-open="keepDialogOpen"
|
||||||
|
title-icon="mdi-bell-alert"
|
||||||
|
:title="$t('general.new') + ' ' + $t('events.notification')"
|
||||||
|
@submit="createNotification"
|
||||||
|
>
|
||||||
|
<template v-slot:open="{ open }">
|
||||||
|
<v-btn small color="info" @click="open">
|
||||||
|
<v-icon left>
|
||||||
|
mdi-plus
|
||||||
|
</v-icon>
|
||||||
|
{{ $t("events.notification") }}
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-slot:default>
|
||||||
|
<v-card-text class="mt-2">
|
||||||
|
{{ $t("events.new-notification-form-description") }}
|
||||||
|
|
||||||
|
<div class="d-flex justify-space-around mt-1 mb-3">
|
||||||
|
<a href="https://github.com/caronc/apprise/wiki" target="_blanks"> Apprise </a>
|
||||||
|
<a href="https://github.com/caronc/apprise/wiki/Notify_gotify" target="_blanks"> Gotify </a>
|
||||||
|
<a href="https://github.com/caronc/apprise/wiki/Notify_discord" target="_blanks"> Discord </a>
|
||||||
|
<a href="https://github.com/caronc/apprise/wiki/Notify_homeassistant" target="_blanks">
|
||||||
|
Home Assistant
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/caronc/apprise/wiki/Notify_matrix" target="_blanks"> Matrix </a>
|
||||||
|
<a href="https://github.com/caronc/apprise/wiki/Notify_pushover" target="_blanks"> Pushover </a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<v-form ref="notificationForm">
|
||||||
|
<v-select
|
||||||
|
:label="$t('general.type')"
|
||||||
|
:rules="[existsRule]"
|
||||||
|
:items="notificationTypes"
|
||||||
|
item-value="text"
|
||||||
|
v-model="newNotification.type"
|
||||||
|
>
|
||||||
|
</v-select>
|
||||||
|
<v-text-field :rules="[existsRule]" :label="$t('general.name')" v-model="newNotification.name">
|
||||||
|
</v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
required
|
||||||
|
:rules="[existsRule]"
|
||||||
|
:label="$t('events.apprise-url')"
|
||||||
|
v-model="newNotification.notificationUrl"
|
||||||
|
>
|
||||||
|
</v-text-field>
|
||||||
|
<v-btn class="d-flex ml-auto" small color="info" @click="testByURL(newNotification.notificationUrl)">
|
||||||
|
<v-icon left> mdi-test-tube</v-icon>
|
||||||
|
{{ $t("general.test") }}
|
||||||
|
</v-btn>
|
||||||
|
<v-subheader class="pa-0 mb-0">
|
||||||
|
{{ $t("events.subscribed-events") }}
|
||||||
|
</v-subheader>
|
||||||
|
<v-row class="mt-1">
|
||||||
|
<v-col cols="3" v-for="(item, key, index) in newNotificationOptions" :key="index">
|
||||||
|
<v-checkbox class="my-n3 py-0" v-model="newNotificationOptions[key]" :label="key"> </v-checkbox>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
</template>
|
||||||
|
</BaseDialog>
|
||||||
|
</v-card-actions>
|
||||||
|
<v-simple-table>
|
||||||
|
<template v-slot:default>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("general.type") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("general.name") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("general.general") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("general.recipe") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("events.database") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("events.scheduled") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("settings.migrations") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("group.group") }}
|
||||||
|
</th>
|
||||||
|
<th class="text-center">
|
||||||
|
{{ $t("user.user") }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(item, index) in notifications" :key="index">
|
||||||
|
<td>
|
||||||
|
<v-avatar size="35" class="ma-1" :color="getIcon(item.type).icon ? 'primary' : undefined">
|
||||||
|
<v-icon dark v-if="getIcon(item.type).icon"> {{ getIcon(item.type).icon }}</v-icon>
|
||||||
|
<v-img v-else :src="getIcon(item.type).image"> </v-img>
|
||||||
|
</v-avatar>
|
||||||
|
{{ item.type }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ item.name }}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<v-icon color="success"> {{ item.general ? "mdi-check" : "" }} </v-icon>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<v-icon color="success"> {{ item.recipe ? "mdi-check" : "" }} </v-icon>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<v-icon color="success"> {{ item.backup ? "mdi-check" : "" }} </v-icon>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<v-icon color="success"> {{ item.scheduled ? "mdi-check" : "" }} </v-icon>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<v-icon color="success"> {{ item.migration ? "mdi-check" : "" }} </v-icon>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<v-icon color="success"> {{ item.group ? "mdi-check" : "" }} </v-icon>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<v-icon color="success"> {{ item.user ? "mdi-check" : "" }} </v-icon>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<v-btn class="mx-1" small color="error" @click="deleteNotification(item.id)">
|
||||||
|
<v-icon> mdi-delete </v-icon>
|
||||||
|
{{ $t("general.delete") }}
|
||||||
|
</v-btn>
|
||||||
|
<v-btn small color="info" @click="testByID(item.id)">
|
||||||
|
<v-icon left> mdi-test-tube</v-icon>
|
||||||
|
{{ $t("general.test") }}
|
||||||
|
</v-btn>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</template>
|
||||||
|
</v-simple-table>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
|
||||||
|
import { api } from "@/api";
|
||||||
|
import { validators } from "@/mixins/validators";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BaseDialog,
|
||||||
|
},
|
||||||
|
mixins: [validators],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
keepDialogOpen: false,
|
||||||
|
notifications: [],
|
||||||
|
newNotification: {
|
||||||
|
type: "General",
|
||||||
|
name: "",
|
||||||
|
notificationUrl: "",
|
||||||
|
},
|
||||||
|
newNotificationOptions: {
|
||||||
|
general: true,
|
||||||
|
recipe: true,
|
||||||
|
backup: true,
|
||||||
|
scheduled: true,
|
||||||
|
migration: true,
|
||||||
|
group: true,
|
||||||
|
user: true,
|
||||||
|
},
|
||||||
|
notificationTypes: [
|
||||||
|
{
|
||||||
|
text: "General",
|
||||||
|
icon: "mdi-bell-alert",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Discord",
|
||||||
|
image: "./static/discord.svg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Gotify",
|
||||||
|
image: "./static/gotify.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Home Assistant",
|
||||||
|
image: "./static/home-assistant.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Pushover",
|
||||||
|
image: "./static/pushover.svg",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getAllNotifications();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getIcon(textValue) {
|
||||||
|
return this.notificationTypes.find(x => x.text === textValue);
|
||||||
|
},
|
||||||
|
async getAllNotifications() {
|
||||||
|
this.notifications = await api.about.allEventNotifications();
|
||||||
|
},
|
||||||
|
async createNotification() {
|
||||||
|
if (this.$refs.notificationForm.validate()) {
|
||||||
|
this.keepDialogOpen = false;
|
||||||
|
await api.about.createNotification({ ...this.newNotification, ...this.newNotificationOptions });
|
||||||
|
this.getAllNotifications();
|
||||||
|
} else {
|
||||||
|
this.keepDialogOpen = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async deleteNotification(id) {
|
||||||
|
await api.about.deleteNotification(id);
|
||||||
|
this.getAllNotifications();
|
||||||
|
},
|
||||||
|
async testByID(id) {
|
||||||
|
await api.about.testNotificationByID(id);
|
||||||
|
},
|
||||||
|
async testByURL(url) {
|
||||||
|
await api.about.testNotificationByURL(url);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -1,221 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<v-card outlined class="mt-n1">
|
|
||||||
<v-card-actions>
|
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<BaseDialog title-icon="mdi-bell-alert" @submit="createNotification" title="New Notification">
|
|
||||||
<template v-slot:open="{ open }">
|
|
||||||
<v-btn small color="info" @click="open">
|
|
||||||
<v-icon left>
|
|
||||||
mdi-plus
|
|
||||||
</v-icon>
|
|
||||||
Notification
|
|
||||||
</v-btn>
|
|
||||||
</template>
|
|
||||||
<v-card-text class="mt-2">
|
|
||||||
We use the <a href="https://github.com/caronc/apprise/wiki" target="_blanks"> Apprise </a> library to
|
|
||||||
generate notifications. They offer many options for services to use for notifications. Refer to their wiki
|
|
||||||
for a comprehensive guide on how to create the URL for your service. If available, selecting the type of
|
|
||||||
your notification can include extra features Here are some common choices.
|
|
||||||
|
|
||||||
<div class="d-flex justify-space-around mt-1">
|
|
||||||
<a href="https://github.com/caronc/apprise/wiki/Notify_gotify" target="_blanks"> Gotify </a>
|
|
||||||
<a href="https://github.com/caronc/apprise/wiki/Notify_discord" target="_blanks"> Discord </a>
|
|
||||||
<a href="https://github.com/caronc/apprise/wiki/Notify_homeassistant" target="_blanks">
|
|
||||||
Home Assistant
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/caronc/apprise/wiki/Notify_matrix" target="_blanks"> Matrix </a>
|
|
||||||
<a href="https://github.com/caronc/apprise/wiki/Notify_pushover" target="_blanks"> Pushover </a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<v-form>
|
|
||||||
<v-select label="Type" :items="notificationTypes" item-value="text" v-model="newNotification.type">
|
|
||||||
</v-select>
|
|
||||||
<v-text-field label="Name" v-model="newNotification.name"> </v-text-field>
|
|
||||||
<v-text-field label="Notification URL" v-model="newNotification.notificationUrl"> </v-text-field>
|
|
||||||
<v-btn class="d-flex ml-auto" small color="info" @click="testByURL(newNotification.notificationUrl)">
|
|
||||||
<v-icon left> mdi-test-tube</v-icon>
|
|
||||||
Test
|
|
||||||
</v-btn>
|
|
||||||
<v-subheader class="pa-0 mb-0">
|
|
||||||
Select the events you would like to recieve notifications for on this URL
|
|
||||||
</v-subheader>
|
|
||||||
<div class="px-3 dflex row justify-space-between mt-3">
|
|
||||||
<v-switch
|
|
||||||
class="ma-0 py-0"
|
|
||||||
v-for="(item, key, index) in newNotificationOptions"
|
|
||||||
:key="index"
|
|
||||||
v-model="newNotificationOptions[key]"
|
|
||||||
:label="key"
|
|
||||||
>
|
|
||||||
</v-switch>
|
|
||||||
</div>
|
|
||||||
</v-form>
|
|
||||||
</v-card-text>
|
|
||||||
</BaseDialog>
|
|
||||||
</v-card-actions>
|
|
||||||
<v-card-text>
|
|
||||||
<v-simple-table>
|
|
||||||
<template v-slot:default>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">
|
|
||||||
Type
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
General
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
Recipe
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
Backup
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
Scheduled
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
Migration
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
Group
|
|
||||||
</th>
|
|
||||||
<th class="text-center">
|
|
||||||
User
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="(item, index) in notifications" :key="index">
|
|
||||||
<td>
|
|
||||||
<v-avatar size="35" class="ma-1" :color="getIcon(item.type).icon ? 'primary' : undefined">
|
|
||||||
<v-icon dark v-if="getIcon(item.type).icon"> {{ getIcon(item.type).icon }}</v-icon>
|
|
||||||
<v-img v-else :src="getIcon(item.type).image"> </v-img>
|
|
||||||
</v-avatar>
|
|
||||||
{{ item.type }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ item.name }}
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<v-icon color="success"> {{ item.general ? "mdi-check" : "" }} </v-icon>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<v-icon color="success"> {{ item.recipe ? "mdi-check" : "" }} </v-icon>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<v-icon color="success"> {{ item.backup ? "mdi-check" : "" }} </v-icon>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<v-icon color="success"> {{ item.scheduled ? "mdi-check" : "" }} </v-icon>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<v-icon color="success"> {{ item.migration ? "mdi-check" : "" }} </v-icon>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<v-icon color="success"> {{ item.group ? "mdi-check" : "" }} </v-icon>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<v-icon color="success"> {{ item.user ? "mdi-check" : "" }} </v-icon>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<v-btn class="mx-1" small color="error" @click="deleteNotification(item.id)">
|
|
||||||
<v-icon> mdi-delete </v-icon>
|
|
||||||
Delete
|
|
||||||
</v-btn>
|
|
||||||
<v-btn small color="info" @click="testByID(item.id)">
|
|
||||||
<v-icon left> mdi-test-tube</v-icon>
|
|
||||||
Test
|
|
||||||
</v-btn>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</template>
|
|
||||||
</v-simple-table>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import BaseDialog from "@/components/UI/Dialogs/BaseDialog";
|
|
||||||
import { api } from "@/api";
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
BaseDialog,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
notifications: [],
|
|
||||||
newNotification: {
|
|
||||||
type: "General",
|
|
||||||
name: "",
|
|
||||||
notificationUrl: "",
|
|
||||||
},
|
|
||||||
newNotificationOptions: {
|
|
||||||
general: true,
|
|
||||||
recipe: true,
|
|
||||||
backup: true,
|
|
||||||
scheduled: true,
|
|
||||||
migration: true,
|
|
||||||
group: true,
|
|
||||||
user: true,
|
|
||||||
},
|
|
||||||
notificationTypes: [
|
|
||||||
{
|
|
||||||
text: "General",
|
|
||||||
icon: "mdi-webhook",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Discord",
|
|
||||||
image: "./static/discord.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Gotify",
|
|
||||||
image: "./static/gotify.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Home Assistant",
|
|
||||||
image: "./static/home-assistant.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Pushover",
|
|
||||||
image: "./static/pushover.svg",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getAllNotifications();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getIcon(textValue) {
|
|
||||||
return this.notificationTypes.find(x => x.text === textValue);
|
|
||||||
},
|
|
||||||
|
|
||||||
async getAllNotifications() {
|
|
||||||
this.notifications = await api.about.allEventNotifications();
|
|
||||||
},
|
|
||||||
async createNotification() {
|
|
||||||
await api.about.createNotification({ ...this.newNotification, ...this.newNotificationOptions });
|
|
||||||
this.getAllNotifications();
|
|
||||||
},
|
|
||||||
async deleteNotification(id) {
|
|
||||||
await api.about.deleteNotification(id);
|
|
||||||
this.getAllNotifications();
|
|
||||||
},
|
|
||||||
async testByID(id) {
|
|
||||||
await api.about.testNotificationByID(id);
|
|
||||||
},
|
|
||||||
async testByURL(url) {
|
|
||||||
await api.about.testNotificationByURL(url);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
</style>
|
|
|
@ -6,9 +6,9 @@ from mealie.db.database import db
|
||||||
from mealie.db.db_setup import generate_session
|
from mealie.db.db_setup import generate_session
|
||||||
from mealie.routes.deps import get_current_user
|
from mealie.routes.deps import get_current_user
|
||||||
from mealie.schema.event_notifications import EventNotificationIn, EventNotificationOut
|
from mealie.schema.event_notifications import EventNotificationIn, EventNotificationOut
|
||||||
from mealie.schema.events import Event, EventCategory, EventsOut, TestEvent
|
from mealie.schema.events import EventsOut, TestEvent
|
||||||
from mealie.schema.user import UserInDB
|
from mealie.schema.user import UserInDB
|
||||||
from mealie.services.events import post_notifications
|
from mealie.services.events import test_notification
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
router = APIRouter(prefix="/events", tags=["App Events"])
|
router = APIRouter(prefix="/events", tags=["App Events"])
|
||||||
|
@ -52,7 +52,7 @@ async def create_event_notification(
|
||||||
|
|
||||||
|
|
||||||
@router.post("/notifications/test")
|
@router.post("/notifications/test")
|
||||||
async def test_notification(
|
async def test_notification_route(
|
||||||
test_data: TestEvent,
|
test_data: TestEvent,
|
||||||
session: Session = Depends(generate_session),
|
session: Session = Depends(generate_session),
|
||||||
current_user: UserInDB = Depends(get_current_user),
|
current_user: UserInDB = Depends(get_current_user),
|
||||||
|
@ -60,17 +60,11 @@ async def test_notification(
|
||||||
""" Create event_notification in the Database """
|
""" Create event_notification in the Database """
|
||||||
|
|
||||||
if test_data.id:
|
if test_data.id:
|
||||||
print("TEST ID")
|
|
||||||
event_obj: EventNotificationIn = db.event_notifications.get(session, test_data.id)
|
event_obj: EventNotificationIn = db.event_notifications.get(session, test_data.id)
|
||||||
test_data.test_url = event_obj.notification_url
|
test_data.test_url = event_obj.notification_url
|
||||||
|
|
||||||
test_event = Event(
|
|
||||||
title="Test Notification",
|
|
||||||
text="This is a test message from the Mealie API server",
|
|
||||||
category=EventCategory.general.value,
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
post_notifications(test_event, [test_data.test_url])
|
test_notification(test_data.test_url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||||
|
|
|
@ -8,6 +8,8 @@ class DeclaredTypes(str, Enum):
|
||||||
general = "General"
|
general = "General"
|
||||||
discord = "Discord"
|
discord = "Discord"
|
||||||
gotify = "Gotify"
|
gotify = "Gotify"
|
||||||
|
pushover = "Pushover"
|
||||||
|
home_assistant = "Home Assistant"
|
||||||
|
|
||||||
|
|
||||||
class EventNotificationOut(CamelModel):
|
class EventNotificationOut(CamelModel):
|
||||||
|
|
|
@ -5,12 +5,27 @@ from mealie.schema.events import Event, EventCategory
|
||||||
from sqlalchemy.orm.session import Session
|
from sqlalchemy.orm.session import Session
|
||||||
|
|
||||||
|
|
||||||
def post_notifications(event: Event, notification_urls=list[str]):
|
def test_notification(notification_url, event=None) -> bool:
|
||||||
|
|
||||||
|
if event is None:
|
||||||
|
event = Event(
|
||||||
|
title="Test Notification",
|
||||||
|
text="This is a test message from the Mealie API server",
|
||||||
|
category=EventCategory.general.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
post_notifications(event, [notification_url], hard_fail=True)
|
||||||
|
|
||||||
|
|
||||||
|
def post_notifications(event: Event, notification_urls=list[str], hard_fail=False):
|
||||||
asset = apprise.AppriseAsset(async_mode=False)
|
asset = apprise.AppriseAsset(async_mode=False)
|
||||||
apobj = apprise.Apprise(asset=asset)
|
apobj = apprise.Apprise(asset=asset)
|
||||||
|
|
||||||
for dest in notification_urls:
|
for dest in notification_urls:
|
||||||
apobj.add(dest)
|
status = apobj.add(dest)
|
||||||
|
|
||||||
|
if not status and hard_fail:
|
||||||
|
raise Exception("Apprise URL Add Failed")
|
||||||
|
|
||||||
apobj.notify(
|
apobj.notify(
|
||||||
body=event.text,
|
body=event.text,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue