@cocreate/notification 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/client.js +18 -23
- package/src/server.js +104 -39
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# [1.1.0](https://github.com/CoCreate-app/CoCreate-notification/compare/v1.0.1...v1.1.0) (2023-10-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* generates Vapid keys per client and returns publicKey to client, the public key is used to create a subscription object and return to server ([0267c19](https://github.com/CoCreate-app/CoCreate-notification/commit/0267c1904a6e6d03c73e4c767cb39f26287776d9))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Use subscription.endpoint to get the apikey ([5fdb66d](https://github.com/CoCreate-app/CoCreate-notification/commit/5fdb66d52ca996ca096f3c935a75bd98c812a9cc))
|
|
12
|
+
|
|
1
13
|
## [1.0.1](https://github.com/CoCreate-app/CoCreate-notification/compare/v1.0.0...v1.0.1) (2023-09-24)
|
|
2
14
|
|
|
3
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/notification",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Simple HTML5 & JavaScript component add, update & remove values in element's notification from input, select or js api. Easily configured using HTML5 notification and/or JavaScript API.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cocreate",
|
package/src/client.js
CHANGED
|
@@ -10,14 +10,9 @@ async function handlePushPermission() {
|
|
|
10
10
|
const registration = await navigator.serviceWorker.ready;
|
|
11
11
|
try {
|
|
12
12
|
// Create a new subscription or retrieve the existing one
|
|
13
|
-
|
|
13
|
+
let subscription = await registration.pushManager.getSubscription();
|
|
14
14
|
|
|
15
|
-
if (
|
|
16
|
-
// Update the existing subscription if needed
|
|
17
|
-
// This might involve generating a new public key or other changes
|
|
18
|
-
// For this example, we'll assume no updates are needed
|
|
19
|
-
console.log('Subscription already exists:', existingSubscription);
|
|
20
|
-
} else {
|
|
15
|
+
if (!subscription) {
|
|
21
16
|
//TODO: getPublicKey
|
|
22
17
|
let response = await socket.send({
|
|
23
18
|
method: 'notification.publicKey',
|
|
@@ -27,31 +22,31 @@ async function handlePushPermission() {
|
|
|
27
22
|
return
|
|
28
23
|
|
|
29
24
|
// Create a new subscription
|
|
30
|
-
|
|
25
|
+
subscription = await registration.pushManager.subscribe({
|
|
31
26
|
userVisibleOnly: true,
|
|
32
27
|
applicationServerKey: response.publicKey,
|
|
33
28
|
});
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
method: 'notification.subscription',
|
|
37
|
-
subscription: newSubscription
|
|
38
|
-
})
|
|
30
|
+
}
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
await socket.send({
|
|
33
|
+
method: 'notification.subscription',
|
|
34
|
+
subscription: subscription
|
|
35
|
+
})
|
|
41
36
|
|
|
42
|
-
|
|
43
|
-
// Handle the subscription change here
|
|
44
|
-
const newSubscription = event.newSubscription;
|
|
45
|
-
const oldSubscription = event.oldSubscription;
|
|
37
|
+
console.log('New subscription created:', subscription);
|
|
46
38
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
navigator.serviceWorker.addEventListener('pushsubscriptionchange', (event) => {
|
|
40
|
+
// Handle the subscription change here
|
|
41
|
+
const newSubscription = event.newSubscription;
|
|
42
|
+
const oldSubscription = event.oldSubscription;
|
|
51
43
|
|
|
52
|
-
|
|
44
|
+
socket.send({
|
|
45
|
+
method: 'notification.subscription',
|
|
46
|
+
subscription: subscription
|
|
47
|
+
})
|
|
53
48
|
|
|
54
|
-
}
|
|
49
|
+
});
|
|
55
50
|
} catch (error) {
|
|
56
51
|
console.error('Error handling push subscription:', error);
|
|
57
52
|
}
|
package/src/server.js
CHANGED
|
@@ -24,66 +24,99 @@ class CoCreateNotification {
|
|
|
24
24
|
let subscription = this.subscriptions.has(data.clientId)
|
|
25
25
|
if (!subscription) {
|
|
26
26
|
// let newKeys = this.generateVapidKeys()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
console.log('Before: ', newKeys.publicKey)
|
|
30
|
-
webpush.setVapidDetails(
|
|
31
|
-
'mailto:contact@cocreate.app',
|
|
32
|
-
newKeys.publicKey,
|
|
33
|
-
newKeys.privateKey
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
// Request a VAPID key
|
|
37
|
-
const vapidRequest = webpush.generateRequestDetails();
|
|
38
|
-
console.log('vapidRequest: ', vapidRequest)
|
|
39
|
-
|
|
40
|
-
data.publicKey = this.base64URLEncode(base64URLEncode(vapidRequest.keys.p256dh))
|
|
41
|
-
this.newKeyMap.set(data.clientId, { publicKey: data.publicKey, privateKey: this.base64URLEncode(vapidRequest.keys.auth) })
|
|
42
|
-
} else {
|
|
43
|
-
data.publicKey = subscription.publicKey
|
|
27
|
+
subscription = webpush.generateVAPIDKeys()
|
|
28
|
+
this.newKeyMap.set(data.clientId, subscription)
|
|
44
29
|
}
|
|
45
30
|
|
|
46
|
-
console.log('
|
|
31
|
+
console.log('subscription: ', subscription)
|
|
32
|
+
|
|
33
|
+
data.publicKey = subscription.publicKey
|
|
47
34
|
if (data.socket)
|
|
48
35
|
this.socket.send(data)
|
|
49
|
-
|
|
50
36
|
}
|
|
51
37
|
|
|
52
38
|
async subscription(data) {
|
|
53
39
|
let newKeys = this.newKeyMap.get(data.clientId)
|
|
54
40
|
if (newKeys) {
|
|
55
41
|
this.newKeyMap.delete(data.clientId)
|
|
42
|
+
|
|
56
43
|
this.subscriptions.set(data.clientId, { ...newKeys, ...data.subscription })
|
|
57
44
|
} else {
|
|
58
45
|
newKeys = this.subscriptions.get(data.clientId)
|
|
59
46
|
if (newKeys) {
|
|
60
47
|
newKeys = { ...newKeys, ...data.subscription }
|
|
61
48
|
} else {
|
|
62
|
-
newKeys = {
|
|
49
|
+
newKeys = await this.crud.send({
|
|
50
|
+
method: 'read.object',
|
|
51
|
+
array: 'clients',
|
|
52
|
+
object: {
|
|
53
|
+
_id: data.clientId
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
if (newKeys && newKeys.object && newKeys.object[0]) {
|
|
57
|
+
newKeys = newKeys.object[0]
|
|
58
|
+
newKeys = { ...newKeys, ...data.subscription }
|
|
59
|
+
this.subscriptions.set(data.clientId, { ...newKeys, ...data.subscription })
|
|
60
|
+
} else {
|
|
61
|
+
this.publicKey(data)
|
|
62
|
+
this.subscription(data)
|
|
63
|
+
}
|
|
63
64
|
}
|
|
64
|
-
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
method: 'update.object',
|
|
68
|
-
array: 'keys',
|
|
69
|
-
object: {
|
|
70
|
-
...data.subscription,
|
|
71
|
-
...newKeys
|
|
72
|
-
}
|
|
73
|
-
});
|
|
66
|
+
}
|
|
74
67
|
|
|
68
|
+
if (newKeys) {
|
|
69
|
+
let tokenOptions = {
|
|
70
|
+
vapidDetails: {
|
|
71
|
+
subject: 'mailto:contact@cocreate.app',
|
|
72
|
+
publicKey: newKeys.publicKey,
|
|
73
|
+
privateKey: newKeys.privateKey
|
|
74
|
+
},
|
|
75
|
+
// Other optional options can be included here
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const jwt = webpush.generateRequestDetails(data.subscription, null, tokenOptions);
|
|
79
|
+
|
|
80
|
+
this.crud.send({
|
|
81
|
+
method: 'update.object',
|
|
82
|
+
array: 'clients',
|
|
83
|
+
object: {
|
|
84
|
+
_id: data.clientId,
|
|
85
|
+
...data.subscription,
|
|
86
|
+
...newKeys,
|
|
87
|
+
jwt
|
|
88
|
+
},
|
|
89
|
+
upsert: true,
|
|
90
|
+
organization_id: data.organization_id
|
|
91
|
+
});
|
|
92
|
+
}
|
|
75
93
|
};
|
|
76
94
|
|
|
77
95
|
// Load the client's subscription object from storage
|
|
78
|
-
send(data) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
async send(data) {
|
|
97
|
+
let subscription = this.subscriptions.get(data.clientId)
|
|
98
|
+
if (!subscription) {
|
|
99
|
+
try {
|
|
100
|
+
subscription = await this.crud.send({
|
|
101
|
+
method: 'read.object',
|
|
102
|
+
array: 'clients',
|
|
103
|
+
object: {
|
|
104
|
+
_id: data.clientId
|
|
105
|
+
},
|
|
106
|
+
organization_id: data.organization_id
|
|
107
|
+
|
|
108
|
+
})
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.log(error)
|
|
111
|
+
}
|
|
112
|
+
if (subscription && subscription.object && subscription.object[0])
|
|
113
|
+
subscription = subscription.object[0]
|
|
114
|
+
}
|
|
82
115
|
if (!subscription || !subscription.privateKey)
|
|
83
116
|
return
|
|
84
117
|
|
|
85
118
|
const cutoffDate = new Date();
|
|
86
|
-
cutoffDate.setDate(cutoffDate.getDate() -
|
|
119
|
+
cutoffDate.setDate(cutoffDate.getDate() - 90);
|
|
87
120
|
|
|
88
121
|
// Compare the modification time with the cutoff date
|
|
89
122
|
if (!this.newKeyMap.has(data.clientId) && subscription.privateKeyCreatedOn < cutoffDate) {
|
|
@@ -94,15 +127,27 @@ class CoCreateNotification {
|
|
|
94
127
|
if (data.payload && !data.payload.timestamp)
|
|
95
128
|
data.payload.timestamp = Date.now()
|
|
96
129
|
|
|
97
|
-
let payload =
|
|
98
|
-
delete payload.privateKey
|
|
99
|
-
delete payload.publicKey
|
|
100
|
-
|
|
130
|
+
// let payload = data.payload
|
|
131
|
+
// delete payload.privateKey
|
|
132
|
+
// delete payload.publicKey
|
|
133
|
+
|
|
134
|
+
let tokenOptions = {
|
|
135
|
+
vapidDetails: {
|
|
136
|
+
subject: 'mailto:contact@cocreate.app',
|
|
137
|
+
publicKey: subscription.publicKey,
|
|
138
|
+
privateKey: subscription.privateKey
|
|
139
|
+
},
|
|
140
|
+
// Other optional options can be included here
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const jwt = webpush.generateRequestDetails(subscription.endpoint, payload, tokenOptions);
|
|
144
|
+
const apiKey = getAPIKeyForEndpoint(subscrption.endpoint);
|
|
145
|
+
|
|
101
146
|
|
|
102
147
|
const options = {
|
|
103
148
|
method: 'POST',
|
|
104
149
|
headers: {
|
|
105
|
-
'Authorization': `Bearer ${
|
|
150
|
+
'Authorization': `Bearer ${apiKey || jwt.headers.Authorization}`,
|
|
106
151
|
'Content-Type': 'application/json',
|
|
107
152
|
},
|
|
108
153
|
};
|
|
@@ -119,6 +164,26 @@ class CoCreateNotification {
|
|
|
119
164
|
pushRequest.end();
|
|
120
165
|
}
|
|
121
166
|
|
|
167
|
+
getAPIKeyForEndpoint(endpoint) {
|
|
168
|
+
// Define the mapping of browser endpoints to API keys
|
|
169
|
+
const browserEndpointsAndAPIKeys = {
|
|
170
|
+
'https://fcm.googleapis.com/': 'yourFCMApiKey',
|
|
171
|
+
'https://updates.push.services.mozilla.com/wpush/v2/': 'yourMozillaPushServiceApiKey',
|
|
172
|
+
'https://api.push.apple.com/': 'yourAPNsApiKey', // For Safari on Mac
|
|
173
|
+
'https://api.sandbox.push.apple.com/': 'yourAPNsDevApiKey', // For Safari on iOS (Development)
|
|
174
|
+
'https://api.push.apple.com/': 'yourAPNsProdApiKey', // For Safari on iOS (Production)
|
|
175
|
+
// Add more browser endpoints and their API keys as needed
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// Iterate through the keys in the mapping
|
|
179
|
+
for (const browserEndpoint of Object.keys(browserEndpointsAndAPIKeys)) {
|
|
180
|
+
if (endpoint.startsWith(browserEndpoint)) {
|
|
181
|
+
// Return the associated API key if the endpoint starts with a known browser endpoint
|
|
182
|
+
return browserEndpointsAndAPIKeys[browserEndpoint];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
122
187
|
generateVapidKeys() {
|
|
123
188
|
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
|
|
124
189
|
modulusLength: 4096, // Choose an appropriate key length
|