updates push notifications
This commit is contained in:
55
cloud/dracattus/api/collection/removePushSubscription.js
Normal file
55
cloud/dracattus/api/collection/removePushSubscription.js
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Remove Push Notification Subscription
|
||||
*
|
||||
* This cloud function removes or deactivates a user's push notification subscription.
|
||||
*/
|
||||
|
||||
Parse.Cloud.define("removePushSubscription", async (request) => {
|
||||
const user = request.user;
|
||||
|
||||
console.log('removePushSubscription called for user:', user?.id);
|
||||
|
||||
// Validation
|
||||
if (!user) {
|
||||
throw new Parse.Error(401, 'User must be authenticated');
|
||||
}
|
||||
|
||||
try {
|
||||
// Find all active subscriptions for this user
|
||||
const PushSubscription = Parse.Object.extend("PUSH_SUBSCRIPTIONS");
|
||||
const query = new Parse.Query(PushSubscription);
|
||||
query.equalTo("user", user);
|
||||
query.equalTo("active", true);
|
||||
|
||||
const subscriptions = await query.find({ useMasterKey: true });
|
||||
|
||||
if (subscriptions.length === 0) {
|
||||
return {
|
||||
success: true,
|
||||
message: 'No active subscriptions found',
|
||||
removed: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Deactivate all subscriptions for this user
|
||||
const promises = subscriptions.map(subscription => {
|
||||
subscription.set("active", false);
|
||||
subscription.set("deactivatedAt", new Date());
|
||||
return subscription.save(null, { useMasterKey: true });
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
console.log(`Deactivated ${subscriptions.length} push subscriptions for user:`, user.id);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Successfully deactivated ${subscriptions.length} push subscription(s)`,
|
||||
removed: subscriptions.length
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error removing push subscription:', error);
|
||||
throw new Parse.Error(500, `Failed to remove push subscription: ${error.message}`);
|
||||
}
|
||||
});
|
||||
70
cloud/dracattus/api/collection/savePushSubscription.js
Normal file
70
cloud/dracattus/api/collection/savePushSubscription.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Save Push Notification Subscription
|
||||
*
|
||||
* This cloud function saves a user's push notification subscription
|
||||
* to enable sending web push notifications for game events.
|
||||
*/
|
||||
|
||||
Parse.Cloud.define("savePushSubscription", async (request) => {
|
||||
const { subscription, userAgent, timestamp } = request.params;
|
||||
const user = request.user;
|
||||
|
||||
console.log('savePushSubscription called for user:', user?.id);
|
||||
|
||||
// Validation
|
||||
if (!user) {
|
||||
throw new Parse.Error(401, 'User must be authenticated');
|
||||
}
|
||||
|
||||
if (!subscription || !subscription.endpoint || !subscription.keys) {
|
||||
throw new Parse.Error(400, 'Invalid subscription data');
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if subscription already exists for this user
|
||||
const PushSubscription = Parse.Object.extend("PUSH_SUBSCRIPTIONS");
|
||||
const query = new Parse.Query(PushSubscription);
|
||||
query.equalTo("user", user);
|
||||
query.equalTo("endpoint", subscription.endpoint);
|
||||
|
||||
let subscriptionObj = await query.first({ useMasterKey: true });
|
||||
|
||||
if (subscriptionObj) {
|
||||
// Update existing subscription
|
||||
subscriptionObj.set("keys", subscription.keys);
|
||||
subscriptionObj.set("userAgent", userAgent);
|
||||
subscriptionObj.set("lastUpdated", new Date());
|
||||
subscriptionObj.set("active", true);
|
||||
} else {
|
||||
// Create new subscription
|
||||
subscriptionObj = new PushSubscription();
|
||||
subscriptionObj.set("user", user);
|
||||
subscriptionObj.set("endpoint", subscription.endpoint);
|
||||
subscriptionObj.set("keys", subscription.keys);
|
||||
subscriptionObj.set("userAgent", userAgent);
|
||||
subscriptionObj.set("createdAt", new Date());
|
||||
subscriptionObj.set("lastUpdated", new Date());
|
||||
subscriptionObj.set("active", true);
|
||||
|
||||
// Set ACL so only the user can read/write their subscription
|
||||
const acl = new Parse.ACL(user);
|
||||
acl.setPublicReadAccess(false);
|
||||
acl.setPublicWriteAccess(false);
|
||||
subscriptionObj.setACL(acl);
|
||||
}
|
||||
|
||||
await subscriptionObj.save(null, { useMasterKey: true });
|
||||
|
||||
console.log('Push subscription saved successfully for user:', user.id);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Push subscription saved successfully',
|
||||
subscriptionId: subscriptionObj.id
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error saving push subscription:', error);
|
||||
throw new Parse.Error(500, `Failed to save push subscription: ${error.message}`);
|
||||
}
|
||||
});
|
||||
319
cloud/dracattus/api/collection/sendPushNotification.js
Normal file
319
cloud/dracattus/api/collection/sendPushNotification.js
Normal file
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* Send Push Notifications
|
||||
*
|
||||
* Cloud functions for sending web push notifications to users.
|
||||
*/
|
||||
|
||||
let webpush;
|
||||
try {
|
||||
webpush = require('web-push');
|
||||
} catch (error) {
|
||||
console.error('web-push module not found. Run: npm install web-push');
|
||||
webpush = null;
|
||||
}
|
||||
|
||||
// VAPID keys configuration (in production, use environment variables)
|
||||
const vapidKeys = {
|
||||
publicKey: 'BAQnBrOv7KWtyZwEIm8kWElTidAYRTEIvuAc0XHvOPbEn31aD34wh3iXx4M87C9lsgMofFc1KDoBFOZZXRfdm-c',
|
||||
privateKey: process.env.VAPID_PRIVATE_KEY || 's2O0GNbimWmWzEMTSEKl0kKr_FYh8mmejrF8-Hr1Qxo'
|
||||
};
|
||||
|
||||
// Configure web-push
|
||||
if (webpush) {
|
||||
webpush.setVapidDetails(
|
||||
'mailto:admin@dracattus.com',
|
||||
vapidKeys.publicKey,
|
||||
vapidKeys.privateKey
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Test Push Notification
|
||||
*/
|
||||
Parse.Cloud.define("sendTestPushNotification", async (request) => {
|
||||
const user = request.user;
|
||||
|
||||
console.log('sendTestPushNotification called for user:', user?.id);
|
||||
|
||||
if (!user) {
|
||||
throw new Parse.Error(401, 'User must be authenticated');
|
||||
}
|
||||
|
||||
if (!webpush) {
|
||||
throw new Parse.Error(500, 'Push notification service not available. Missing web-push dependency.');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await sendPushToUser(user.id, {
|
||||
title: '🐉 Dracattus Test Notification',
|
||||
body: 'Web push notifications are now working! Your dragons await your return.',
|
||||
icon: '/assets/icon/dracattus-icon-256.png',
|
||||
badge: '/assets/icon/dracattus-icon-72.png',
|
||||
data: {
|
||||
type: 'test',
|
||||
timestamp: new Date().toISOString(),
|
||||
url: '/'
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Test notification sent successfully',
|
||||
details: result
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error sending test notification:', error);
|
||||
throw new Parse.Error(500, `Failed to send test notification: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Send Trade Offer Notification
|
||||
*/
|
||||
Parse.Cloud.define("sendTradeOfferNotification", async (request) => {
|
||||
const { userId, assetName, assetId, offerCreator } = request.params;
|
||||
|
||||
console.log('sendTradeOfferNotification called:', { userId, assetName, assetId });
|
||||
|
||||
if (!userId || !assetName || !assetId) {
|
||||
throw new Parse.Error(400, 'Missing required parameters');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await sendPushToUser(userId, {
|
||||
title: '💰 New Trade Offer!',
|
||||
body: `Someone wants to trade for your ${assetName}!`,
|
||||
icon: '/assets/icon/dracattus-icon-256.png',
|
||||
badge: '/assets/icon/dracattus-icon-72.png',
|
||||
data: {
|
||||
type: 'trade_offer',
|
||||
assetId: assetId,
|
||||
assetName: assetName,
|
||||
offerCreator: offerCreator,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Trade offer notification sent',
|
||||
details: result
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error sending trade offer notification:', error);
|
||||
throw new Parse.Error(500, `Failed to send trade offer notification: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Send Battle Result Notification
|
||||
*/
|
||||
Parse.Cloud.define("sendBattleResultNotification", async (request) => {
|
||||
const { userId, battleResult, opponentName } = request.params;
|
||||
|
||||
console.log('sendBattleResultNotification called:', { userId, battleResult, opponentName });
|
||||
|
||||
if (!userId || !battleResult) {
|
||||
throw new Parse.Error(400, 'Missing required parameters');
|
||||
}
|
||||
|
||||
const isWin = battleResult === 'win';
|
||||
const title = isWin ? '🎉 Victory!' : '⚔️ Battle Result';
|
||||
const body = isWin
|
||||
? `Congratulations! You defeated ${opponentName || 'your opponent'}!`
|
||||
: `Your battle against ${opponentName || 'your opponent'} has concluded.`;
|
||||
|
||||
try {
|
||||
const result = await sendPushToUser(userId, {
|
||||
title,
|
||||
body,
|
||||
icon: '/assets/icon/dracattus-icon-256.png',
|
||||
badge: '/assets/icon/dracattus-icon-72.png',
|
||||
data: {
|
||||
type: 'battle_result',
|
||||
result: battleResult,
|
||||
opponentName: opponentName,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Battle result notification sent',
|
||||
details: result
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error sending battle result notification:', error);
|
||||
throw new Parse.Error(500, `Failed to send battle result notification: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Send Breeding Complete Notification
|
||||
*/
|
||||
Parse.Cloud.define("sendBreedingCompleteNotification", async (request) => {
|
||||
const { userId, newDracattusName, newDracattusId } = request.params;
|
||||
|
||||
console.log('sendBreedingCompleteNotification called:', { userId, newDracattusName, newDracattusId });
|
||||
|
||||
if (!userId || !newDracattusName || !newDracattusId) {
|
||||
throw new Parse.Error(400, 'Missing required parameters');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await sendPushToUser(userId, {
|
||||
title: '🥚 New Dragon Born!',
|
||||
body: `Your breeding is complete! Welcome ${newDracattusName} to your collection.`,
|
||||
icon: '/assets/icon/dracattus-icon-256.png',
|
||||
badge: '/assets/icon/dracattus-icon-72.png',
|
||||
data: {
|
||||
type: 'breeding_complete',
|
||||
newAssetId: newDracattusId,
|
||||
newAssetName: newDracattusName,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Breeding complete notification sent',
|
||||
details: result
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error sending breeding complete notification:', error);
|
||||
throw new Parse.Error(500, `Failed to send breeding complete notification: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Send Quest Available Notification
|
||||
*/
|
||||
Parse.Cloud.define("sendQuestNotification", async (request) => {
|
||||
const { userId, questTitle, questDescription, questId } = request.params;
|
||||
|
||||
console.log('sendQuestNotification called:', { userId, questTitle, questId });
|
||||
|
||||
if (!userId || !questTitle) {
|
||||
throw new Parse.Error(400, 'Missing required parameters');
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await sendPushToUser(userId, {
|
||||
title: '⚡ New Quest Available!',
|
||||
body: questTitle,
|
||||
icon: '/assets/icon/dracattus-icon-256.png',
|
||||
badge: '/assets/icon/dracattus-icon-72.png',
|
||||
data: {
|
||||
type: 'new_quest',
|
||||
questId: questId,
|
||||
questTitle: questTitle,
|
||||
questDescription: questDescription,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Quest notification sent',
|
||||
details: result
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error sending quest notification:', error);
|
||||
throw new Parse.Error(500, `Failed to send quest notification: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Core function to send push notification to a specific user
|
||||
*/
|
||||
async function sendPushToUser(userId, notificationData) {
|
||||
console.log('Sending push notification to user:', userId);
|
||||
|
||||
try {
|
||||
// Find active push subscriptions for the user
|
||||
const User = Parse.Object.extend("User");
|
||||
const userQuery = new Parse.Query(User);
|
||||
const user = await userQuery.get(userId, { useMasterKey: true });
|
||||
|
||||
if (!user) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
const PushSubscription = Parse.Object.extend("PUSH_SUBSCRIPTIONS");
|
||||
const subscriptionQuery = new Parse.Query(PushSubscription);
|
||||
subscriptionQuery.equalTo("user", user);
|
||||
subscriptionQuery.equalTo("active", true);
|
||||
|
||||
const subscriptions = await subscriptionQuery.find({ useMasterKey: true });
|
||||
|
||||
if (subscriptions.length === 0) {
|
||||
console.log('No active push subscriptions found for user:', userId);
|
||||
return {
|
||||
sent: 0,
|
||||
message: 'No active subscriptions'
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`Found ${subscriptions.length} active subscriptions for user:`, userId);
|
||||
|
||||
// Send notification to all active subscriptions
|
||||
const results = [];
|
||||
const errors = [];
|
||||
|
||||
for (const subscription of subscriptions) {
|
||||
try {
|
||||
const pushSubscription = {
|
||||
endpoint: subscription.get('endpoint'),
|
||||
keys: subscription.get('keys')
|
||||
};
|
||||
|
||||
const payload = JSON.stringify(notificationData);
|
||||
|
||||
await webpush.sendNotification(pushSubscription, payload);
|
||||
results.push({
|
||||
subscriptionId: subscription.id,
|
||||
status: 'sent'
|
||||
});
|
||||
|
||||
console.log('Push notification sent successfully to subscription:', subscription.id);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error sending to subscription:', subscription.id, error);
|
||||
errors.push({
|
||||
subscriptionId: subscription.id,
|
||||
error: error.message
|
||||
});
|
||||
|
||||
// If subscription is invalid, deactivate it
|
||||
if (error.statusCode === 410 || error.statusCode === 404) {
|
||||
subscription.set('active', false);
|
||||
subscription.set('deactivatedAt', new Date());
|
||||
subscription.set('deactivationReason', 'Invalid subscription');
|
||||
await subscription.save(null, { useMasterKey: true });
|
||||
console.log('Deactivated invalid subscription:', subscription.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
sent: results.length,
|
||||
failed: errors.length,
|
||||
results: results,
|
||||
errors: errors
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in sendPushToUser:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Export the function for use in other cloud functions
|
||||
module.exports = {
|
||||
sendPushToUser
|
||||
};
|
||||
61
package-lock.json
generated
61
package-lock.json
generated
@@ -32,6 +32,7 @@
|
||||
"parse-server-api-mail-adapter": "^2.2.0",
|
||||
"pdfkit": "^0.16.0",
|
||||
"sharp": "^0.34.3",
|
||||
"web-push": "^3.6.7",
|
||||
"web3": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -3160,7 +3161,6 @@
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
@@ -3330,6 +3330,18 @@
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asn1.js": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
|
||||
"integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bn.js": "^4.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/assert-options": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/assert-options/-/assert-options-0.8.1.tgz",
|
||||
@@ -3618,6 +3630,12 @@
|
||||
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bn.js": {
|
||||
"version": "4.12.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz",
|
||||
"integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
@@ -6822,6 +6840,15 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/http_ece": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz",
|
||||
"integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
@@ -6942,7 +6969,6 @@
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"agent-base": "^7.1.2",
|
||||
"debug": "4"
|
||||
@@ -6956,7 +6982,6 @@
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
@@ -6973,8 +6998,7 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/humanize-ms": {
|
||||
"version": "1.2.1",
|
||||
@@ -7678,7 +7702,6 @@
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
|
||||
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
@@ -7730,7 +7753,6 @@
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1"
|
||||
@@ -8185,6 +8207,12 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
@@ -12830,6 +12858,25 @@
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/web-push": {
|
||||
"version": "3.6.7",
|
||||
"resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz",
|
||||
"integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"asn1.js": "^5.3.0",
|
||||
"http_ece": "1.2.0",
|
||||
"https-proxy-agent": "^7.0.0",
|
||||
"jws": "^4.0.0",
|
||||
"minimist": "^1.2.5"
|
||||
},
|
||||
"bin": {
|
||||
"web-push": "src/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"parse-server-api-mail-adapter": "^2.2.0",
|
||||
"pdfkit": "^0.16.0",
|
||||
"sharp": "^0.34.3",
|
||||
"web-push": "^3.6.7",
|
||||
"web3": "^4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Reference in New Issue
Block a user