Page MenuHomePhabricator

D6817.id22827.diff
No OneTemporary

D6817.id22827.diff

diff --git a/keyserver/src/push/providers.js b/keyserver/src/push/providers.js
--- a/keyserver/src/push/providers.js
+++ b/keyserver/src/push/providers.js
@@ -101,6 +101,13 @@
return cachedWebPushConfig;
}
+async function ensureWebPushInitialized() {
+ if (cachedWebPushConfig) {
+ return;
+ }
+ await getWebPushConfig();
+}
+
export {
getAPNPushProfileForCodeVersion,
getFCMPushProfileForCodeVersion,
@@ -110,4 +117,5 @@
endAPNs,
getAPNsNotificationTopic,
getWebPushConfig,
+ ensureWebPushInitialized,
};
diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js
--- a/keyserver/src/push/send.js
+++ b/keyserver/src/push/send.js
@@ -39,6 +39,7 @@
getUnreadCounts,
apnMaxNotificationPayloadByteSize,
fcmMaxNotificationPayloadByteSize,
+ webPush,
} from './utils.js';
import createIDs from '../creators/id-creator.js';
import { createUpdates } from '../creators/update-creator.js';
@@ -59,7 +60,7 @@
+devices: Device[],
+messageInfos: RawMessageInfo[],
};
-type Delivery = IOSDelivery | AndroidDelivery | { collapsedInto: string };
+type Delivery = PushDelivery | { collapsedInto: string };
type NotificationRow = {
+dbID: string,
+userID: string,
@@ -213,6 +214,23 @@
deliveryPromises.push(deliveryPromise);
}
}
+ const webVersionsToTokens = byPlatform.get('web');
+ if (webVersionsToTokens) {
+ for (const [codeVersion, deviceTokens] of webVersionsToTokens) {
+ const deliveryPromise = (async () => {
+ const notification = await prepareWebNotification(
+ allMessageInfos,
+ threadInfo,
+ unreadCounts[userID],
+ );
+ return await sendWebNotification(notification, [...deviceTokens], {
+ ...notificationInfo,
+ codeVersion,
+ });
+ })();
+ deliveryPromises.push(deliveryPromise);
+ }
+ }
for (const newMessageInfo of remainingNewMessageInfos) {
const newDBID = dbIDs.shift();
@@ -247,7 +265,7 @@
// The results in deliveryResults will be combined with the rows
// in rowsToSave and then written to the notifications table
async function saveNotifResults(
- deliveryResults: $ReadOnlyArray<IOSResult | AndroidResult>,
+ deliveryResults: $ReadOnlyArray<PushResult>,
inputRowsToSave: Map<string, NotificationRow>,
rescindable: boolean,
) {
@@ -596,6 +614,26 @@
return notification;
}
+async function prepareWebNotification(
+ allMessageInfos: MessageInfo[],
+ threadInfo: ThreadInfo,
+ unreadCount: number,
+): Promise<Object> {
+ const id = uuidv4();
+ const { merged, ...rest } = await notifTextsForMessageInfo(
+ allMessageInfos,
+ threadInfo,
+ getENSNames,
+ );
+ const notification = {
+ ...rest,
+ unreadCount,
+ id,
+ threadID: threadInfo.id,
+ };
+ return notification;
+}
+
type NotificationInfo =
| {
+source: 'new_message',
@@ -653,6 +691,8 @@
return result;
}
+type PushResult = AndroidResult | IOSResult | WebResult;
+type PushDelivery = AndroidDelivery | IOSDelivery | WebDelivery;
type AndroidDelivery = {
source: $PropertyType<NotificationInfo, 'source'>,
deviceType: 'android',
@@ -702,6 +742,49 @@
return result;
}
+type WebDelivery = {
+ source: $PropertyType<NotificationInfo, 'source'>,
+ deviceType: 'web',
+ deviceTokens: $ReadOnlyArray<string>,
+ codeVersion?: number,
+ errors?: $ReadOnlyArray<Object>,
+};
+type WebResult = {
+ info: NotificationInfo,
+ delivery: WebDelivery,
+ invalidTokens?: $ReadOnlyArray<string>,
+};
+async function sendWebNotification(
+ notification: Object,
+ deviceTokens: $ReadOnlyArray<string>,
+ notificationInfo: NotificationInfo,
+): Promise<WebResult> {
+ const { source, codeVersion } = notificationInfo;
+
+ const response = await webPush({
+ notification,
+ deviceTokens,
+ });
+
+ const delivery: WebDelivery = {
+ source,
+ deviceType: 'web',
+ deviceTokens,
+ codeVersion,
+ };
+ if (response.errors) {
+ delivery.errors = response.errors;
+ }
+ const result: WebResult = {
+ info: notificationInfo,
+ delivery,
+ };
+ if (response.invalidTokens) {
+ result.invalidTokens = response.invalidTokens;
+ }
+ return result;
+}
+
type InvalidToken = {
+userID: string,
+tokens: $ReadOnlyArray<string>,
diff --git a/keyserver/src/push/utils.js b/keyserver/src/push/utils.js
--- a/keyserver/src/push/utils.js
+++ b/keyserver/src/push/utils.js
@@ -4,6 +4,7 @@
import type { ResponseFailure } from '@parse/node-apn';
import type { FirebaseApp, FirebaseError } from 'firebase-admin';
import invariant from 'invariant';
+import webpush from 'web-push';
import { threadSubscriptions } from 'lib/types/subscription-types.js';
import { threadPermissions } from 'lib/types/thread-types.js';
@@ -13,6 +14,7 @@
getFCMPushProfileForCodeVersion,
getAPNProvider,
getFCMProvider,
+ ensureWebPushInitialized,
} from './providers.js';
import { dbQuery, SQL } from '../database/database.js';
@@ -198,9 +200,69 @@
return usersToUnreadCounts;
}
+type WebPushResult = {
+ +success?: true,
+ +errors?: $ReadOnlyArray<Object>,
+ +invalidTokens?: $ReadOnlyArray<string>,
+};
+async function webPush({
+ notification,
+ deviceTokens,
+}: {
+ +notification: Object,
+ +deviceTokens: $ReadOnlyArray<string>,
+}): Promise<WebPushResult> {
+ await ensureWebPushInitialized();
+ const notificationString = JSON.stringify(notification);
+
+ const promises = [];
+ for (const deviceTokenString of deviceTokens) {
+ const deviceToken: PushSubscriptionJSON = JSON.parse(deviceTokenString);
+ promises.push(
+ (async () => {
+ try {
+ await webpush.sendNotification(deviceToken, notificationString);
+ } catch (error) {
+ return { error };
+ }
+ return {};
+ })(),
+ );
+ }
+
+ const pushResults = await Promise.all(promises);
+
+ const errors = [];
+ const invalidTokens = [];
+ for (let i = 0; i < pushResults.length; i++) {
+ const pushResult = pushResults[i];
+ if (pushResult.error) {
+ errors.push(pushResult.error);
+ if (
+ pushResult.error.statusCode === 404 ||
+ pushResult.error.statusCode === 410
+ ) {
+ invalidTokens.push(deviceTokens[i]);
+ }
+ }
+ }
+
+ const result = {};
+ if (errors.length > 0) {
+ result.errors = errors;
+ } else {
+ result.success = true;
+ }
+ if (invalidTokens.length > 0) {
+ result.invalidTokens = invalidTokens;
+ }
+ return { ...result };
+}
+
export {
apnPush,
fcmPush,
+ webPush,
getUnreadCounts,
apnMaxNotificationPayloadByteSize,
fcmMaxNotificationPayloadByteSize,

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 22, 7:03 PM (18 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2692184
Default Alt Text
D6817.id22827.diff (6 KB)

Event Timeline