-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Expand file tree
/
Copy pathapn.ts
More file actions
159 lines (135 loc) · 4.63 KB
/
apn.ts
File metadata and controls
159 lines (135 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import apn from '@parse/node-apn';
import type { RequiredField } from '@rocket.chat/core-typings';
import EJSON from 'ejson';
import type { PushOptions, PendingPushNotification } from './definition';
import { logger } from './logger';
let apnConnection: apn.Provider | undefined;
declare module '@parse/node-apn' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Notification {
setContentAvailable: (value: boolean | 1 | 0) => void;
set category(_value: string | undefined);
set body(_value: string);
set title(_value: string);
}
}
export const sendAPN = ({
userToken,
notification,
_removeToken,
}: {
userToken: string;
notification: PendingPushNotification & { topic: string };
_removeToken: (token: string) => void;
}) => {
if (!apnConnection) {
throw new Error('Apn Connection not initialized.');
}
const priority = notification.priority || notification.priority === 0 ? notification.priority : 10;
const note = new apn.Notification();
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;
}
if (notification.sound !== undefined) {
note.sound = notification.sound;
}
if (notification.contentAvailable != null) {
note.setContentAvailable(notification.contentAvailable);
}
// 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
if (notification.apn?.category) {
note.category = notification.apn.category;
}
if (notification.text) {
note.body = notification.text;
}
if (notification.title) {
note.title = notification.title;
}
if (notification.notId != null) {
note.threadId = String(notification.notId);
}
// Allow the user to set payload data
note.payload = notification.payload ? { ejson: EJSON.stringify(notification.payload) } : {};
if (notification.from) {
note.payload.messageFrom = notification.from;
}
note.priority = priority;
note.topic = notification.topic;
note.mutableContent = true;
void apnConnection.send(note, userToken).then((response) => {
response.failed.forEach((failure) => {
logger.debug({
msg: 'Got error code for APN token',
status: failure.status,
token: userToken,
});
if (['400', '410'].includes(String(failure.status))) {
logger.debug({
msg: 'Removing APN token',
token: userToken,
});
_removeToken(userToken);
}
});
});
};
export const initAPN = ({ options, absoluteUrl }: { options: RequiredField<PushOptions, 'apn'>; absoluteUrl: string }) => {
logger.debug('APN configured');
if (options.apn.gateway) {
// We check the apn gateway i the options, we could risk shipping
// server into production while using the production configuration.
// On the other hand we could be in development but using the production
// configuration. And finally we could have configured an unknown apn
// gateway (this could change in the future - but a warning about typos
// can save hours of debugging)
//
// Warn about gateway configurations - it's more a guide
if (options.apn.gateway === 'gateway.sandbox.push.apple.com') {
// Using the development sandbox
logger.warn('WARNING: Push APN is in development mode');
} else if (options.apn.gateway === 'gateway.push.apple.com') {
// In production - but warn if we are running on localhost
if (/http:\/\/localhost/.test(absoluteUrl)) {
logger.warn('WARNING: Push APN is configured to production mode - but server is running from localhost');
}
} else {
// Warn about gateways we dont know about
logger.warn({
msg: 'WARNING: Push APN unknown gateway',
gateway: options.apn.gateway,
});
}
} else if (options.production) {
if (/http:\/\/localhost/.test(absoluteUrl)) {
logger.warn('WARNING: Push APN is configured to production mode - but server is running from localhost');
}
} else {
logger.warn('WARNING: Push APN is in development mode');
}
// Check certificate data
if (!options.apn.cert?.length) {
logger.error('ERROR: Push server could not find cert');
}
// Check key data
if (!options.apn.key?.length) {
logger.error('ERROR: Push server could not find key');
}
// Rig apn connection
try {
apnConnection = new apn.Provider({
...options.apn,
production: options.production,
});
} catch (err) {
logger.error({ msg: 'Error trying to initialize APN', err });
}
};