mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-30 17:53:31 -04:00 
			
		
		
		
	events api
This commit is contained in:
		| @@ -0,0 +1,42 @@ | ||||
| import { requests } from "../requests"; | ||||
| import { BaseCRUDAPI } from "./_base"; | ||||
|  | ||||
| export type EventCategory = "general" | "recipe" | "backup" | "scheduled" | "migration" | "group" | "user"; | ||||
| export type DeclaredTypes = "General" | "Discord" | "Gotify" | "Pushover" | "Home Assistant"; | ||||
| export type GotifyPriority = "low" | "moderate" | "normal" | "high"; | ||||
|  | ||||
| export interface EventNotification { | ||||
|   id?: number; | ||||
|   name?: string; | ||||
|   type?: DeclaredTypes & string; | ||||
|   general?: boolean; | ||||
|   recipe?: boolean; | ||||
|   backup?: boolean; | ||||
|   scheduled?: boolean; | ||||
|   migration?: boolean; | ||||
|   group?: boolean; | ||||
|   user?: boolean; | ||||
| } | ||||
|  | ||||
| export interface CreateEventNotification extends EventNotification { | ||||
|   notificationUrl?: string; | ||||
| } | ||||
|  | ||||
| const prefix = "/api"; | ||||
|  | ||||
| const routes = { | ||||
|   aboutEventsNotifications: `${prefix}/about/events/notifications`, | ||||
|   aboutEventsNotificationsTest: `${prefix}/about/events/notifications/test`, | ||||
|  | ||||
|   aboutEventsNotificationsId: (id: number) => `${prefix}/about/events/notifications/${id}`, | ||||
| }; | ||||
|  | ||||
| export class NotificationsAPI extends BaseCRUDAPI<EventNotification, CreateEventNotification> { | ||||
|   baseRoute = routes.aboutEventsNotifications; | ||||
|   itemRoute = routes.aboutEventsNotificationsId; | ||||
|   /** Returns the Group Data for the Current User | ||||
|    */ | ||||
|   async testNotification(id: number) { | ||||
|     return await requests.post(routes.aboutEventsNotificationsTest, { id }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import { UploadFile } from "./class-interfaces/upload"; | ||||
| import { CategoriesAPI } from "./class-interfaces/categories"; | ||||
| import { TagsAPI } from "./class-interfaces/tags"; | ||||
| import { UtilsAPI } from "./class-interfaces/utils"; | ||||
| import { NotificationsAPI } from "./class-interfaces/event-notifications"; | ||||
| import { ApiRequestInstance } from "~/types/api"; | ||||
|  | ||||
| class Api { | ||||
| @@ -21,6 +22,7 @@ class Api { | ||||
|   public categories: CategoriesAPI; | ||||
|   public tags: TagsAPI; | ||||
|   public utils: UtilsAPI; | ||||
|   public notifications: NotificationsAPI; | ||||
|  | ||||
|   // Utils | ||||
|   public upload: UploadFile; | ||||
| @@ -43,6 +45,7 @@ class Api { | ||||
|     this.debug = new DebugAPI(requests); | ||||
|     this.events = new EventsAPI(requests); | ||||
|     this.backups = new BackupAPI(requests); | ||||
|     this.notifications = new NotificationsAPI(requests); | ||||
|  | ||||
|     // Utils | ||||
|     this.upload = new UploadFile(requests); | ||||
|   | ||||
							
								
								
									
										88
									
								
								frontend/composables/use-notifications.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								frontend/composables/use-notifications.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| import { useAsync, ref } from "@nuxtjs/composition-api"; | ||||
| import { CreateEventNotification } from "@/api/class-interfaces/event-notifications"; | ||||
| import { useAsyncKey } from "./use-utils"; | ||||
| import { useApiSingleton } from "~/composables/use-api"; | ||||
|  | ||||
| const notificationTypes = ["General", "Discord", "Gotify", "Pushover", "Home Assistant"]; | ||||
|  | ||||
|  | ||||
| export const useNotifications = function () { | ||||
|   const api = useApiSingleton(); | ||||
|   const loading = ref(false); | ||||
|  | ||||
|   const createNotificationData = ref<CreateEventNotification>({ | ||||
|     name: "", | ||||
|     type: "General", | ||||
|     general: true, | ||||
|     recipe: true, | ||||
|     backup: true, | ||||
|     scheduled: true, | ||||
|     migration: true, | ||||
|     group: true, | ||||
|     user: true, | ||||
|     notificationUrl: "", | ||||
|   }); | ||||
|  | ||||
|   const deleteTarget = ref(0) | ||||
|  | ||||
|   function getNotifications() { | ||||
|     loading.value = true; | ||||
|     const notifications = useAsync(async () => { | ||||
|       const { data } = await api.notifications.getAll(); | ||||
|       return data; | ||||
|     }, useAsyncKey()); | ||||
|     loading.value = false; | ||||
|     return notifications; | ||||
|   } | ||||
|  | ||||
|   async function refreshNotifications() { | ||||
|     loading.value = true; | ||||
|     const { data } = await api.notifications.getAll(); | ||||
|     if (data) { | ||||
|       notifications.value = data; | ||||
|     } | ||||
|     loading.value = false; | ||||
|   } | ||||
|  | ||||
|   async function createNotification() { | ||||
|     if (createNotificationData.value.name === "" || createNotificationData.value.notificationUrl === "") { | ||||
|       return; | ||||
|     } | ||||
|     const { response } = await api.notifications.createOne(createNotificationData.value); | ||||
|  | ||||
|     if (response?.status === 200) { | ||||
|       refreshNotifications(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async function deleteNotification() { | ||||
|     const { response } = await api.notifications.deleteOne(deleteTarget.value); | ||||
|     if (response?.status === 200) { | ||||
|       refreshNotifications(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   async function testById() { | ||||
|       // TODO: Test by ID | ||||
|   } | ||||
|  | ||||
|   async function testByUrl() { | ||||
|       // TODO: Test by URL | ||||
|   } | ||||
|  | ||||
|   const notifications = getNotifications(); | ||||
|  | ||||
|   return { | ||||
|     createNotification, | ||||
|     deleteNotification, | ||||
|     refreshNotifications, | ||||
|     getNotifications, | ||||
|     testById, | ||||
|     testByUrl, | ||||
|     notifications, | ||||
|     loading, | ||||
|     createNotificationData, | ||||
|     notificationTypes, | ||||
|     deleteTarget, | ||||
|   }; | ||||
| }; | ||||
| @@ -2,17 +2,213 @@ | ||||
|   <v-container fluid> | ||||
|     <BaseCardSectionTitle title="Event Notifications"> | ||||
|       {{ $t("events.new-notification-form-description") }} | ||||
|  | ||||
|       <div class="d-flex justify-space-around"> | ||||
|         <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> | ||||
|     </BaseCardSectionTitle> | ||||
|  | ||||
|     <BaseDialog | ||||
|       ref="domDeleteConfirmation" | ||||
|       :title="$t('settings.backup.delete-backup')" | ||||
|       color="error" | ||||
|       :icon="$globals.icons.alertCircle" | ||||
|       @confirm="deleteNotification()" | ||||
|     > | ||||
|       <v-card-text> | ||||
|         {{ $t("general.confirm-delete-generic") }} | ||||
|       </v-card-text> | ||||
|     </BaseDialog> | ||||
|  | ||||
|     <v-toolbar flat class="justify-between"> | ||||
|       <BaseDialog | ||||
|         :icon="$globals.icons.bellAlert" | ||||
|         :title="$t('general.new') + ' ' + $t('events.notification')" | ||||
|         @submit="createNotification" | ||||
|       > | ||||
|         <template #activator="{ open }"> | ||||
|           <BaseButton @click="open"> {{ $t("events.notification") }}</BaseButton> | ||||
|         </template> | ||||
|  | ||||
|         <v-card-text> | ||||
|           <v-select | ||||
|             v-model="createNotificationData.type" | ||||
|             :items="notificationTypes" | ||||
|             :label="$t('general.type')" | ||||
|           ></v-select> | ||||
|           <v-text-field v-model="createNotificationData.name" :label="$t('general.name')"></v-text-field> | ||||
|           <v-text-field | ||||
|             v-model="createNotificationData.notificationUrl" | ||||
|             :label="$t('events.apprise-url')" | ||||
|           ></v-text-field> | ||||
|  | ||||
|           <BaseButton class="d-flex ml-auto" small color="info" @click="testByUrl(newNotification.notificationUrl)"> | ||||
|             <template #icon> {{ $globals.icons.testTube }}</template> | ||||
|             {{ $t("general.test") }} | ||||
|           </BaseButton> | ||||
|  | ||||
|           <p class="text-uppercase">{{ $t("events.subscribed-events") }}</p> | ||||
|           <div class="d-flex flex-wrap justify-center"> | ||||
|             <v-checkbox | ||||
|               v-model="createNotificationData.general" | ||||
|               class="mb-n2 mt-n2 mx-2" | ||||
|               :label="$t('general.general')" | ||||
|             ></v-checkbox> | ||||
|             <v-checkbox | ||||
|               v-model="createNotificationData.recipe" | ||||
|               class="mb-n2 mt-n2 mx-2" | ||||
|               :label="$t('general.recipe')" | ||||
|             ></v-checkbox> | ||||
|             <v-checkbox | ||||
|               v-model="createNotificationData.backup" | ||||
|               class="mb-n2 mt-n2 mx-2" | ||||
|               :label="$t('settings.backup-and-exports')" | ||||
|             ></v-checkbox> | ||||
|             <v-checkbox | ||||
|               v-model="createNotificationData.scheduled" | ||||
|               class="mb-n2 mt-n2 mx-2" | ||||
|               :label="$t('events.scheduled')" | ||||
|             ></v-checkbox> | ||||
|             <v-checkbox | ||||
|               v-model="createNotificationData.migration" | ||||
|               class="mb-n2 mt-n2 mx-2" | ||||
|               :label="$t('settings.migrations')" | ||||
|             ></v-checkbox> | ||||
|             <v-checkbox | ||||
|               v-model="createNotificationData.group" | ||||
|               class="mb-n2 mt-n2 mx-2" | ||||
|               :label="$t('group.group')" | ||||
|             ></v-checkbox> | ||||
|             <v-checkbox | ||||
|               v-model="createNotificationData.user" | ||||
|               class="mb-n2 mt-n2 mx-2" | ||||
|               :label="$t('user.user')" | ||||
|             ></v-checkbox> | ||||
|           </div> | ||||
|         </v-card-text> | ||||
|       </BaseDialog> | ||||
|     </v-toolbar> | ||||
|  | ||||
|     <!-- Data Table --> | ||||
|     <v-data-table | ||||
|       :headers="headers" | ||||
|       :items="notifications || []" | ||||
|       class="elevation-0" | ||||
|       hide-default-footer | ||||
|       disable-pagination | ||||
|     > | ||||
|       <template v-for="boolHeader in headers" #[`item.${boolHeader.value}`]="{ item }"> | ||||
|         <div :key="boolHeader.value"> | ||||
|           <div v-if="boolHeader.value === 'type'"> | ||||
|             {{ item[boolHeader.value] }} | ||||
|           </div> | ||||
|           <v-icon | ||||
|             v-else-if="item[boolHeader.value] === true || item[boolHeader.value] === false" | ||||
|             :color="item[boolHeader.value] ? 'success' : 'gray'" | ||||
|           > | ||||
|             {{ item[boolHeader.value] ? $globals.icons.check : $globals.icons.close }} | ||||
|           </v-icon> | ||||
|           <div v-else-if="boolHeader.value === 'actions'" class="d-flex"> | ||||
|             <BaseButton | ||||
|               class="mr-1" | ||||
|               delete | ||||
|               x-small | ||||
|               minor | ||||
|               @click=" | ||||
|                 deleteTarget = item.id; | ||||
|                 domDeleteConfirmation.open(); | ||||
|               " | ||||
|             /> | ||||
|             <BaseButton edit x-small @click="testById(item.id)"> | ||||
|               <template #icon> | ||||
|                 {{ $globals.icons.testTube }} | ||||
|               </template> | ||||
|               {{ $t("general.test") }} | ||||
|             </BaseButton> | ||||
|           </div> | ||||
|           <div v-else> | ||||
|             {{ item[boolHeader.value] }} | ||||
|           </div> | ||||
|         </div> | ||||
|       </template> | ||||
|     </v-data-table> | ||||
|   </v-container> | ||||
| </template> | ||||
|      | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from "@nuxtjs/composition-api"; | ||||
|  | ||||
| import { defineComponent, reactive, useContext, toRefs, ref } from "@nuxtjs/composition-api"; | ||||
| import { useNotifications } from "@/composables/use-notifications"; | ||||
| export default defineComponent({ | ||||
|   layout: "admin", | ||||
|   setup() { | ||||
|     return {}; | ||||
|     // @ts-ignore -> Ignore missing $globals | ||||
|     const { i18n } = useContext(); | ||||
|  | ||||
|     const state = reactive({ | ||||
|       headers: [ | ||||
|         { text: i18n.t("general.type"), value: "type" }, | ||||
|         { text: i18n.t("general.name"), value: "name" }, | ||||
|         { text: i18n.t("general.general"), value: "general", align: "center" }, | ||||
|         { text: i18n.t("general.recipe"), value: "recipe", align: "center" }, | ||||
|         { text: i18n.t("events.database"), value: "backup", align: "center" }, | ||||
|         { text: i18n.t("events.scheduled"), value: "scheduled", align: "center" }, | ||||
|         { text: i18n.t("settings.migrations"), value: "migration", align: "center" }, | ||||
|         { text: i18n.t("group.group"), value: "group", align: "center" }, | ||||
|         { text: i18n.t("user.user"), value: "user", align: "center" }, | ||||
|         { text: "", value: "actions" }, | ||||
|       ], | ||||
|       keepDialogOpen: false, | ||||
|       notifications: [], | ||||
|       newNotification: { | ||||
|         type: "General", | ||||
|         name: "", | ||||
|         notificationUrl: "", | ||||
|       }, | ||||
|       newNotificationOptions: { | ||||
|         general: true, | ||||
|         recipe: true, | ||||
|         backup: true, | ||||
|         scheduled: true, | ||||
|         migration: true, | ||||
|         group: true, | ||||
|         user: true, | ||||
|       }, | ||||
|     }); | ||||
|  | ||||
|     const { | ||||
|       deleteNotification, | ||||
|       createNotification, | ||||
|       refreshNotifications, | ||||
|       notifications, | ||||
|       loading, | ||||
|       testById, | ||||
|       testByUrl, | ||||
|       createNotificationData, | ||||
|       notificationTypes, | ||||
|       deleteTarget, | ||||
|     } = useNotifications(); | ||||
|  | ||||
|     // API | ||||
|     const domDeleteConfirmation = ref(null); | ||||
|     return { | ||||
|       ...toRefs(state), | ||||
|       domDeleteConfirmation, | ||||
|       notifications, | ||||
|       loading, | ||||
|       createNotificationData, | ||||
|       deleteNotification, | ||||
|       deleteTarget, | ||||
|       createNotification, | ||||
|       refreshNotifications, | ||||
|       testById, | ||||
|       testByUrl, | ||||
|       notificationTypes, | ||||
|     }; | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user