Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
27 changes: 21 additions & 6 deletions apps/meteor/app/api/server/v1/push.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Push } from '@rocket.chat/core-services';
import type { IPushToken } from '@rocket.chat/core-typings';
import { pushTokenTypes } from '@rocket.chat/core-typings';
import type { IPushToken, IPushTokenTypes } from '@rocket.chat/core-typings';
import { Messages, PushToken, Users, Rooms, Settings } from '@rocket.chat/models';
import {
ajv,
Expand All @@ -22,9 +23,10 @@ import type { SuccessResult } from '../definition';

type PushTokenPOST = {
id?: string;
type: 'apn' | 'gcm';
type: IPushTokenTypes;
value: string;
appName: string;
voipToken?: string;
};

const PushTokenPOSTSchema: JSONSchemaType<PushTokenPOST> = {
Expand All @@ -36,7 +38,7 @@ const PushTokenPOSTSchema: JSONSchemaType<PushTokenPOST> = {
},
type: {
type: 'string',
enum: ['apn', 'gcm'],
enum: pushTokenTypes,
},
value: {
type: 'string',
Expand All @@ -46,6 +48,10 @@ const PushTokenPOSTSchema: JSONSchemaType<PushTokenPOST> = {
type: 'string',
minLength: 1,
},
voipToken: {
type: 'string',
nullable: true,
},
},
required: ['type', 'value', 'appName'],
additionalProperties: false,
Expand All @@ -71,13 +77,13 @@ const PushTokenDELETESchema: JSONSchemaType<PushTokenDELETE> = {

export const isPushTokenDELETEProps = ajv.compile<PushTokenDELETE>(PushTokenDELETESchema);

type PushTokenResult = Pick<IPushToken, '_id' | 'token' | 'appName' | 'userId' | 'enabled' | 'createdAt' | '_updatedAt'>;
type PushTokenResult = Pick<IPushToken, '_id' | 'token' | 'appName' | 'userId' | 'enabled' | 'createdAt' | '_updatedAt' | 'voipToken'>;

/**
* Pick only the attributes we actually want to return on the endpoint, ensuring nothing from older schemas get mixed in
*/
function cleanTokenResult(result: Omit<IPushToken, 'authToken'>): PushTokenResult {
const { _id, token, appName, userId, enabled, createdAt, _updatedAt } = result;
const { _id, token, appName, userId, enabled, createdAt, _updatedAt, voipToken } = result;

return {
_id,
Expand All @@ -87,6 +93,7 @@ function cleanTokenResult(result: Omit<IPushToken, 'authToken'>): PushTokenResul
enabled,
createdAt,
_updatedAt,
voipToken,
};
}

Expand Down Expand Up @@ -139,6 +146,9 @@ const pushTokenEndpoints = API.v1
_updatedAt: {
type: 'string',
},
voipToken: {
type: 'string',
},
},
additionalProperties: false,
},
Expand All @@ -153,7 +163,11 @@ const pushTokenEndpoints = API.v1
authRequired: true,
},
async function action() {
const { id, type, value, appName } = this.bodyParams;
const { id, type, value, appName, voipToken } = this.bodyParams;

if (voipToken && !id) {
return API.v1.failure('voip-tokens-must-specify-device-id');
}

const rawToken = this.request.headers.get('x-auth-token');
if (!rawToken) {
Expand All @@ -167,6 +181,7 @@ const pushTokenEndpoints = API.v1
authToken,
appName,
userId: this.userId,
...(voipToken && { voipToken }),
});

return API.v1.success({ result: cleanTokenResult(result) });
Expand Down
32 changes: 22 additions & 10 deletions apps/meteor/app/push/server/apn.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import apn from '@parse/node-apn';
import type { IPushToken, RequiredField } from '@rocket.chat/core-typings';
import type { RequiredField } from '@rocket.chat/core-typings';
import EJSON from 'ejson';

import type { PushOptions, PendingPushNotification } from './definition';
Expand All @@ -24,7 +24,7 @@ export const sendAPN = ({
}: {
userToken: string;
notification: PendingPushNotification & { topic: string };
_removeToken: (token: IPushToken['token']) => void;
_removeToken: (token: string) => void;
}) => {
if (!apnConnection) {
throw new Error('Apn Connection not initialized.');
Expand All @@ -34,7 +34,13 @@ export const sendAPN = ({

const note = new apn.Notification();

note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
if (notification.useVoipToken) {
note.expiry = Math.floor(Date.now() / 1000) + 60; // Expires in 60 seconds
note.pushType = 'voip';
} else {
note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
}

if (notification.badge !== undefined) {
note.badge = notification.badge;
}
Expand All @@ -50,10 +56,16 @@ export const sendAPN = ({
// adds category support for iOS8 custom actions as described here:
// https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/
// RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html#//apple_ref/doc/uid/TP40008194-CH103-SW36
note.category = notification.apn?.category;
if (notification.apn?.category) {
note.category = notification.apn.category;
}

note.body = notification.text;
note.title = notification.title;
if (notification.text) {
note.body = notification.text;
}
if (notification.title) {
note.title = notification.title;
}

if (notification.notId != null) {
note.threadId = String(notification.notId);
Expand All @@ -62,7 +74,9 @@ export const sendAPN = ({
// Allow the user to set payload data
note.payload = notification.payload ? { ejson: EJSON.stringify(notification.payload) } : {};

note.payload.messageFrom = notification.from;
if (notification.from) {
note.payload.messageFrom = notification.from;
}
note.priority = priority;

note.topic = notification.topic;
Expand All @@ -81,9 +95,7 @@ export const sendAPN = ({
msg: 'Removing APN token',
token: userToken,
});
_removeToken({
apn: userToken,
});
_removeToken(userToken);
}
});
});
Expand Down
7 changes: 4 additions & 3 deletions apps/meteor/app/push/server/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export type PushOptions = {
};

export type PendingPushNotification = {
from: string;
title: string;
text: string;
from?: string;
title?: string;
text?: string;
badge?: number;
sound?: string;
notId?: number;
Expand All @@ -42,4 +42,5 @@ export type PendingPushNotification = {
priority?: number;

contentAvailable?: 1 | 0;
useVoipToken?: boolean;
};
12 changes: 6 additions & 6 deletions apps/meteor/app/push/server/fcm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import type { NativeNotificationParameters } from './push';
type FCMDataField = Record<string, any>;

type FCMNotificationField = {
title: string;
body: string;
title?: string;
body?: string;
image?: string;
};

Expand Down Expand Up @@ -140,13 +140,13 @@ function getFCMMessagesFromPushData(userTokens: string[], notification: PendingP

// then we will create the notification field
const notificationField: FCMNotificationField = {
title: notification.title,
body: notification.text,
...(notification.title && { title: notification.title }),
...(notification.text && { body: notification.text }),
};

// then we will create the message
const message: FCMMessage = {
notification: notificationField,
...(Object.keys(notificationField).length && { notification: notificationField }),
data,
android: {
priority: 'HIGH',
Expand Down Expand Up @@ -185,7 +185,7 @@ export const sendFCM = function ({ userTokens, notification, _removeToken, optio

const removeToken = () => {
const { token } = fcmRequest.message;
token && _removeToken({ gcm: token });
token && _removeToken(token);
};

const response = fetchWithRetry(url, removeToken, {
Expand Down
Loading
Loading