Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix/fix notification bug #89

Merged
merged 3 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion apps/server/src/Services/Notifier/Notifier/DeviceNotifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,35 @@ import {type NotificationParameters} from '../../../Interfaces/NotificationParam
import type Notifier from '../Notifier';
import {NOTIFICATION_METHOD} from '../methodConstants';
import {env} from '../../../env.mjs';
import {logger} from '../../../../src/server/logger';
import {logger} from '../../../server/logger';
import {prisma} from '../../../server/db';

class DeviceNotifier implements Notifier {
getSupportedMethods(): Array<string> {
return [NOTIFICATION_METHOD.DEVICE];
}

async deleteNotificationAndDevice(destination: string, notificationId: string): Promise<void> {
try {
// Delete the notification
await prisma.notification.delete({
where: {
id: notificationId,
},
});
// Unverify and disable the alertMethod
await prisma.alertMethod.deleteMany({
where: {
destination: destination,
method: NOTIFICATION_METHOD.DEVICE,
}
});
logger(`Notification with ID: ${notificationId} deleted and alertMethod for destination: ${destination} has been unverified and disabled.`, "info");
} catch (error) {
logger(`Database Error: Couldn't modify the alertMethod or delete the notification: ${error}`, "error");
}
}

// OneSignal can send both iOS and android notifications,
// "destination" from AlertMethod for method "device"
// is the OneSignal player ID of the device.
Expand Down Expand Up @@ -46,6 +68,10 @@ class DeviceNotifier implements Notifier {
`Failed to send device notification. Error: ${response.statusText} for ${parameters.id}`,
'error',
);
// If device not found
if(response.status === 404){
await this.deleteNotificationAndDevice(destination, parameters.id)
}
return false;
}

Expand Down
45 changes: 43 additions & 2 deletions apps/server/src/Services/Notifier/Notifier/WebhookNotifier.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {prisma} from '../../../server/db';
import {logger} from '../../../../src/server/logger';
import {type NotificationParameters} from '../../../Interfaces/NotificationParameters';
import type Notifier from '../Notifier';
Expand All @@ -8,6 +9,31 @@ class WebhookNotifier implements Notifier {
return [NOTIFICATION_METHOD.WEBHOOK];
}

async deleteNotificationDisableAndUnverifyWebhook(destination: string, notificationId: string): Promise<void> {
try {
// Delete the notification
await prisma.notification.delete({
where: {
id: notificationId,
},
});
// Unverify and disable the alertMethod
await prisma.alertMethod.updateMany({
where: {
destination: destination,
method: NOTIFICATION_METHOD.WEBHOOK,
},
data: {
isVerified: false,
isEnabled: false,
},
});
logger(`Notification with ID: ${notificationId} deleted and alertMethod for destination: ${destination} has been unverified and disabled.`, "info");
} catch (error) {
logger(`Database Error: Couldn't modify the alertMethod or delete the notification: ${error}`, "error");
}
}

async notify(
destination: string,
parameters: NotificationParameters,
Expand All @@ -34,12 +60,27 @@ class WebhookNotifier implements Notifier {

if (!response.ok) {
logger(
`Failed to send webhook notification. Error: ${response.statusText} for ${parameters.id}`,
`Failed to send webhook notification. Error: ${response.statusText} for ${parameters.id}.`,
'error',
);
// Specific status code handling
if (response.status === 404) {
// Webhook URL Not Found - Token not found
await this.deleteNotificationDisableAndUnverifyWebhook(destination, parameters.id);
} else if (response.status === 401){
// Unauthorized
await this.deleteNotificationDisableAndUnverifyWebhook(destination, parameters.id);
} else if (response.status === 403){
// Forbidden
await this.deleteNotificationDisableAndUnverifyWebhook(destination, parameters.id);
} else {
logger(
`Failed to send webhook notification. Something went wrong. Try again in next run.`,
'error',
);
}
return false;
}

return true;
}
}
Expand Down
35 changes: 19 additions & 16 deletions apps/server/src/server/api/zodSchemas/alertMethod.schema.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import { z } from 'zod';
import {z} from 'zod';
import phone from 'phone'
import validator from 'validator';

export const createAlertMethodSchema = z.object({
method: z.enum(["email", "sms", "device", "whatsapp", "webhook"]),
destination: z.string({
required_error: 'Destination of alert method must be specified'
}).refine((value) => {
const sanitized = validator.escape(value);
return sanitized === value;
}, {
message: 'Contains invalid characters',
}),
}),
deviceName: z.string().optional(),
deviceId: z.string().optional(),
}).refine((obj) => {
if (obj.method === 'sms') {
// Check if the destination is a valid phone number in E.164 format
const { isValid } = phone(obj.destination);
return isValid;
}
if (obj.method === 'email') {
return z.string().email().safeParse(obj.destination).success;
if(obj.method === 'webhook'){
return true;
}else{
const sanitized = validator.escape(obj.destination);
if (sanitized !== obj.destination){
return false;
}
if (obj.method === 'sms') {
// Check if the destination is a valid phone number in E.164 format
const { isValid } = phone(obj.destination);
return isValid;
}
if (obj.method === 'email') {
return z.string().email().safeParse(obj.destination).success;
}
return true; // Return true for other methods
}
return true; // Return true for other methods
}, {
message: 'Must be a valid phone number in E.164 format when the method is "sms"'
message: `Invalid Destination`
});

export const params = z.object({
Expand Down
Loading