diff --git a/server/lib/notifications/agents/discord.ts b/server/lib/notifications/agents/discord.ts index 209ac6eb..1b79f7e3 100644 --- a/server/lib/notifications/agents/discord.ts +++ b/server/lib/notifications/agents/discord.ts @@ -272,7 +272,7 @@ class DiscordAgent type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response.data, + response: e.response?.data, }); return false; diff --git a/server/lib/notifications/agents/pushbullet.ts b/server/lib/notifications/agents/pushbullet.ts index c43e9971..ab4b811e 100644 --- a/server/lib/notifications/agents/pushbullet.ts +++ b/server/lib/notifications/agents/pushbullet.ts @@ -170,7 +170,7 @@ class PushbulletAgent type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response.data, + response: e.response?.data, }); return false; diff --git a/server/lib/notifications/agents/pushover.ts b/server/lib/notifications/agents/pushover.ts index f9bff21c..858da0c6 100644 --- a/server/lib/notifications/agents/pushover.ts +++ b/server/lib/notifications/agents/pushover.ts @@ -196,7 +196,7 @@ class PushoverAgent type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response.data, + response: e.response?.data, }); return false; diff --git a/server/lib/notifications/agents/slack.ts b/server/lib/notifications/agents/slack.ts index f9fe46c9..7004fe4b 100644 --- a/server/lib/notifications/agents/slack.ts +++ b/server/lib/notifications/agents/slack.ts @@ -254,7 +254,7 @@ class SlackAgent type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response.data, + response: e.response?.data, }); return false; diff --git a/server/lib/notifications/agents/telegram.ts b/server/lib/notifications/agents/telegram.ts index a97bbb6f..1a22ddce 100644 --- a/server/lib/notifications/agents/telegram.ts +++ b/server/lib/notifications/agents/telegram.ts @@ -244,7 +244,7 @@ class TelegramAgent type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response.data, + response: e.response?.data, }); return false; diff --git a/server/lib/notifications/agents/webhook.ts b/server/lib/notifications/agents/webhook.ts index 7630cf44..7d8cbd86 100644 --- a/server/lib/notifications/agents/webhook.ts +++ b/server/lib/notifications/agents/webhook.ts @@ -154,7 +154,7 @@ class WebhookAgent type: Notification[type], subject: payload.subject, errorMessage: e.message, - response: e.response.data, + response: e.response?.data, }); return false; diff --git a/server/routes/settings/notifications.ts b/server/routes/settings/notifications.ts index 8c60c960..bb21c7b6 100644 --- a/server/routes/settings/notifications.ts +++ b/server/routes/settings/notifications.ts @@ -28,23 +28,30 @@ notificationRoutes.post('/discord', (req, res) => { res.status(200).json(settings.notifications.agents.discord); }); -notificationRoutes.post('/discord/test', (req, res, next) => { +notificationRoutes.post('/discord/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, - message: 'User information missing from request', + message: 'User information is missing from the request.', }); } const discordAgent = new DiscordAgent(req.body); - discordAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await discordAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send Discord notification.', + }); + } }); notificationRoutes.get('/slack', (_req, res) => { @@ -62,23 +69,30 @@ notificationRoutes.post('/slack', (req, res) => { res.status(200).json(settings.notifications.agents.slack); }); -notificationRoutes.post('/slack/test', (req, res, next) => { +notificationRoutes.post('/slack/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, - message: 'User information missing from request', + message: 'User information is missing from the request.', }); } const slackAgent = new SlackAgent(req.body); - slackAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await slackAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send Slack notification.', + }); + } }); notificationRoutes.get('/telegram', (_req, res) => { @@ -96,23 +110,30 @@ notificationRoutes.post('/telegram', (req, res) => { res.status(200).json(settings.notifications.agents.telegram); }); -notificationRoutes.post('/telegram/test', (req, res, next) => { +notificationRoutes.post('/telegram/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, - message: 'User information missing from request', + message: 'User information is missing from the request.', }); } const telegramAgent = new TelegramAgent(req.body); - telegramAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await telegramAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send Telegram notification.', + }); + } }); notificationRoutes.get('/pushbullet', (_req, res) => { @@ -130,23 +151,30 @@ notificationRoutes.post('/pushbullet', (req, res) => { res.status(200).json(settings.notifications.agents.pushbullet); }); -notificationRoutes.post('/pushbullet/test', (req, res, next) => { +notificationRoutes.post('/pushbullet/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, - message: 'User information missing from request', + message: 'User information is missing from the request.', }); } const pushbulletAgent = new PushbulletAgent(req.body); - pushbulletAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await pushbulletAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send Pushbullet notification.', + }); + } }); notificationRoutes.get('/pushover', (_req, res) => { @@ -164,23 +192,30 @@ notificationRoutes.post('/pushover', (req, res) => { res.status(200).json(settings.notifications.agents.pushover); }); -notificationRoutes.post('/pushover/test', (req, res, next) => { +notificationRoutes.post('/pushover/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, - message: 'User information missing from request', + message: 'User information is missing from the request.', }); } const pushoverAgent = new PushoverAgent(req.body); - pushoverAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await pushoverAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send Pushover notification.', + }); + } }); notificationRoutes.get('/email', (_req, res) => { @@ -198,23 +233,30 @@ notificationRoutes.post('/email', (req, res) => { res.status(200).json(settings.notifications.agents.email); }); -notificationRoutes.post('/email/test', (req, res, next) => { +notificationRoutes.post('/email/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, - message: 'User information missing from request', + message: 'User information is missing from the request.', }); } const emailAgent = new EmailAgent(req.body); - emailAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await emailAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send email notification.', + }); + } }); notificationRoutes.get('/webpush', (_req, res) => { @@ -232,7 +274,7 @@ notificationRoutes.post('/webpush', (req, res) => { res.status(200).json(settings.notifications.agents.webpush); }); -notificationRoutes.post('/webpush/test', (req, res, next) => { +notificationRoutes.post('/webpush/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, @@ -241,14 +283,21 @@ notificationRoutes.post('/webpush/test', (req, res, next) => { } const webpushAgent = new WebPushAgent(req.body); - webpushAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await webpushAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send web push notification.', + }); + } }); notificationRoutes.get('/webhook', (_req, res) => { @@ -296,11 +345,11 @@ notificationRoutes.post('/webhook', (req, res, next) => { } }); -notificationRoutes.post('/webhook/test', (req, res, next) => { +notificationRoutes.post('/webhook/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, - message: 'User information missing from request', + message: 'User information is missing from the request.', }); } @@ -320,14 +369,21 @@ notificationRoutes.post('/webhook/test', (req, res, next) => { }; const webhookAgent = new WebhookAgent(testBody); - webhookAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await webhookAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send webhook notification.', + }); + } } catch (e) { next({ status: 500, message: e.message }); } @@ -348,7 +404,7 @@ notificationRoutes.post('/lunasea', (req, res) => { res.status(200).json(settings.notifications.agents.lunasea); }); -notificationRoutes.post('/lunasea/test', (req, res, next) => { +notificationRoutes.post('/lunasea/test', async (req, res, next) => { if (!req.user) { return next({ status: 500, @@ -357,14 +413,21 @@ notificationRoutes.post('/lunasea/test', (req, res, next) => { } const lunaseaAgent = new LunaSeaAgent(req.body); - lunaseaAgent.send(Notification.TEST_NOTIFICATION, { - notifyUser: req.user, - subject: 'Test Notification', - message: - 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', - }); - - return res.status(204).send(); + if ( + await lunaseaAgent.send(Notification.TEST_NOTIFICATION, { + notifyUser: req.user, + subject: 'Test Notification', + message: + 'This is a test notification! Check check, 1, 2, 3. Are we coming in clear?', + }) + ) { + return res.status(204).send(); + } else { + return next({ + status: 500, + message: 'Failed to send web push notification.', + }); + } }); export default notificationRoutes; diff --git a/src/components/Settings/Notifications/NotificationsDiscord.tsx b/src/components/Settings/Notifications/NotificationsDiscord.tsx index b70baf28..f195e9e6 100644 --- a/src/components/Settings/Notifications/NotificationsDiscord.tsx +++ b/src/components/Settings/Notifications/NotificationsDiscord.tsx @@ -1,6 +1,6 @@ import axios from 'axios'; import { Field, Form, Formik } from 'formik'; -import React from 'react'; +import React, { useState } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; import useSWR from 'swr'; @@ -18,13 +18,16 @@ const messages = defineMessages({ webhookUrlPlaceholder: 'Server Settings → Integrations → Webhooks', discordsettingssaved: 'Discord notification settings saved successfully!', discordsettingsfailed: 'Discord notification settings failed to save.', - discordtestsent: 'Discord test notification sent!', + toastDiscordTestSending: 'Sending Discord test notification…', + toastDiscordTestSuccess: 'Discord test notification sent!', + toastDiscordTestFailed: 'Discord test notification failed to send.', validationUrl: 'You must provide a valid URL', }); const NotificationsDiscord: React.FC = () => { const intl = useIntl(); - const { addToast } = useToasts(); + const { addToast, removeToast } = useToasts(); + const [isTesting, setIsTesting] = useState(false); const { data, error, revalidate } = useSWR( '/api/v1/settings/notifications/discord' ); @@ -86,20 +89,47 @@ const NotificationsDiscord: React.FC = () => { > {({ errors, touched, isSubmitting, values, isValid, setFieldValue }) => { const testSettings = async () => { - await axios.post('/api/v1/settings/notifications/discord/test', { - enabled: true, - types: values.types, - options: { - botUsername: values.botUsername, - botAvatarUrl: values.botAvatarUrl, - webhookUrl: values.webhookUrl, - }, - }); + setIsTesting(true); + let toastId: string | undefined; + try { + addToast( + intl.formatMessage(messages.toastDiscordTestSending), + { + autoDismiss: false, + appearance: 'info', + }, + (id) => { + toastId = id; + } + ); + await axios.post('/api/v1/settings/notifications/discord/test', { + enabled: true, + types: values.types, + options: { + botUsername: values.botUsername, + botAvatarUrl: values.botAvatarUrl, + webhookUrl: values.webhookUrl, + }, + }); - addToast(intl.formatMessage(messages.discordtestsent), { - appearance: 'info', - autoDismiss: true, - }); + if (toastId) { + removeToast(toastId); + } + addToast(intl.formatMessage(messages.toastDiscordTestSuccess), { + autoDismiss: true, + appearance: 'success', + }); + } catch (e) { + if (toastId) { + removeToast(toastId); + } + addToast(intl.formatMessage(messages.toastDiscordTestFailed), { + autoDismiss: true, + appearance: 'error', + }); + } finally { + setIsTesting(false); + } }; return ( @@ -178,21 +208,22 @@ const NotificationsDiscord: React.FC = () => {