@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 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.1",
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
- const existingSubscription = await registration.pushManager.getSubscription();
13
+ let subscription = await registration.pushManager.getSubscription();
14
14
 
15
- if (existingSubscription) {
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
- const newSubscription = await registration.pushManager.subscribe({
25
+ subscription = await registration.pushManager.subscribe({
31
26
  userVisibleOnly: true,
32
27
  applicationServerKey: response.publicKey,
33
28
  });
34
29
 
35
- await socket.send({
36
- method: 'notification.subscription',
37
- subscription: newSubscription
38
- })
30
+ }
39
31
 
40
- console.log('New subscription created:', newSubscription);
32
+ await socket.send({
33
+ method: 'notification.subscription',
34
+ subscription: subscription
35
+ })
41
36
 
42
- navigator.serviceWorker.addEventListener('pushsubscriptionchange', (event) => {
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
- socket.send({
48
- method: 'notification.subscription',
49
- subscription: newSubscription
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
- let newKeys = webpush.generateVAPIDKeys()
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('After: ', data.publicKey)
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
- crud.send({
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
- const key = crud.send({ array: 'keys', filter: { query: [{}] } });
80
-
81
- const subscription = key.object[0]
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() - subscription.rotateKeysIn || 90);
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 = { ...subscription, ...data.payload }
98
- delete payload.privateKey
99
- delete payload.publicKey
100
- payload = JSON.stringify(payload);
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 ${this.getVapidJWT(subscription.privateKey)}`,
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