mirror of
https://github.com/lawrencehook/remove-youtube-suggestions.git
synced 2026-07-25 06:54:31 +00:00
Migrate SES sends from v1 to SESv2 to avoid fast-xml-parser conflict
The fast-xml-parser >=5.3.8 override (added for CVE) is incompatible with @aws-sdk/client-ses, whose XML response parser registers entities named "#xD" and "#10" — names that 5.x rejects with [EntityReplacer] Invalid character '#' in entity name. SESv2 uses REST/JSON, sidestepping the parser entirely. validateEmail already used SESv2; this consolidates the three send functions onto the same client and drops @aws-sdk/client-ses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
-65
@@ -8,7 +8,6 @@
|
|||||||
"name": "rys-premium-server",
|
"name": "rys-premium-server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-ses": "^3.1000.0",
|
|
||||||
"@aws-sdk/client-sesv2": "^3.1000.0",
|
"@aws-sdk/client-sesv2": "^3.1000.0",
|
||||||
"better-sqlite3": "^11.0.0",
|
"better-sqlite3": "^11.0.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@@ -151,57 +150,6 @@
|
|||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@aws-sdk/client-ses": {
|
|
||||||
"version": "3.1035.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.1035.0.tgz",
|
|
||||||
"integrity": "sha512-fpyLQVNuBXwkSvySrBsi6P/qS/ZhNhSifY25Hiluo0EP6Zec7OM8SGWR5ECq9M3I87MyHvp1GrzuoWg+9/Fk5g==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@aws-crypto/sha256-browser": "5.2.0",
|
|
||||||
"@aws-crypto/sha256-js": "5.2.0",
|
|
||||||
"@aws-sdk/core": "^3.974.4",
|
|
||||||
"@aws-sdk/credential-provider-node": "^3.972.35",
|
|
||||||
"@aws-sdk/middleware-host-header": "^3.972.10",
|
|
||||||
"@aws-sdk/middleware-logger": "^3.972.10",
|
|
||||||
"@aws-sdk/middleware-recursion-detection": "^3.972.11",
|
|
||||||
"@aws-sdk/middleware-user-agent": "^3.972.34",
|
|
||||||
"@aws-sdk/region-config-resolver": "^3.972.13",
|
|
||||||
"@aws-sdk/types": "^3.973.8",
|
|
||||||
"@aws-sdk/util-endpoints": "^3.996.8",
|
|
||||||
"@aws-sdk/util-user-agent-browser": "^3.972.10",
|
|
||||||
"@aws-sdk/util-user-agent-node": "^3.973.20",
|
|
||||||
"@smithy/config-resolver": "^4.4.17",
|
|
||||||
"@smithy/core": "^3.23.16",
|
|
||||||
"@smithy/fetch-http-handler": "^5.3.17",
|
|
||||||
"@smithy/hash-node": "^4.2.14",
|
|
||||||
"@smithy/invalid-dependency": "^4.2.14",
|
|
||||||
"@smithy/middleware-content-length": "^4.2.14",
|
|
||||||
"@smithy/middleware-endpoint": "^4.4.31",
|
|
||||||
"@smithy/middleware-retry": "^4.5.4",
|
|
||||||
"@smithy/middleware-serde": "^4.2.19",
|
|
||||||
"@smithy/middleware-stack": "^4.2.14",
|
|
||||||
"@smithy/node-config-provider": "^4.3.14",
|
|
||||||
"@smithy/node-http-handler": "^4.6.0",
|
|
||||||
"@smithy/protocol-http": "^5.3.14",
|
|
||||||
"@smithy/smithy-client": "^4.12.12",
|
|
||||||
"@smithy/types": "^4.14.1",
|
|
||||||
"@smithy/url-parser": "^4.2.14",
|
|
||||||
"@smithy/util-base64": "^4.3.2",
|
|
||||||
"@smithy/util-body-length-browser": "^4.2.2",
|
|
||||||
"@smithy/util-body-length-node": "^4.2.3",
|
|
||||||
"@smithy/util-defaults-mode-browser": "^4.3.48",
|
|
||||||
"@smithy/util-defaults-mode-node": "^4.2.53",
|
|
||||||
"@smithy/util-endpoints": "^3.4.2",
|
|
||||||
"@smithy/util-middleware": "^4.2.14",
|
|
||||||
"@smithy/util-retry": "^4.3.3",
|
|
||||||
"@smithy/util-utf8": "^4.2.2",
|
|
||||||
"@smithy/util-waiter": "^4.2.16",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@aws-sdk/client-sesv2": {
|
"node_modules/@aws-sdk/client-sesv2": {
|
||||||
"version": "3.1035.0",
|
"version": "3.1035.0",
|
||||||
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.1035.0.tgz",
|
"resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.1035.0.tgz",
|
||||||
@@ -1330,19 +1278,6 @@
|
|||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@smithy/util-waiter": {
|
|
||||||
"version": "4.2.16",
|
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.16.tgz",
|
|
||||||
"integrity": "sha512-GtclrKoZ3Lt7jPQ7aTIYKfjY92OgceScftVnkTsG8e1KV8rkvZgN+ny6YSRhd9hxB8rZtwVbmln7NTvE5O3GmQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@smithy/types": "^4.14.1",
|
|
||||||
"tslib": "^2.6.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@smithy/uuid": {
|
"node_modules/@smithy/uuid": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz",
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
"test": "node --test --test-concurrency=1 tests/*.test.js"
|
"test": "node --test --test-concurrency=1 tests/*.test.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-ses": "^3.1000.0",
|
|
||||||
"@aws-sdk/client-sesv2": "^3.1000.0",
|
"@aws-sdk/client-sesv2": "^3.1000.0",
|
||||||
"better-sqlite3": "^11.0.0",
|
"better-sqlite3": "^11.0.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
const { SESClient, SendEmailCommand } = require('@aws-sdk/client-ses');
|
const {
|
||||||
const { SESv2Client, GetEmailAddressInsightsCommand } = require('@aws-sdk/client-sesv2');
|
SESv2Client,
|
||||||
|
SendEmailCommand,
|
||||||
|
GetEmailAddressInsightsCommand,
|
||||||
|
} = require('@aws-sdk/client-sesv2');
|
||||||
const config = require('../config');
|
const config = require('../config');
|
||||||
|
|
||||||
const sesClient = new SESClient({ region: config.AWS_REGION });
|
|
||||||
const sesV2Client = new SESv2Client({ region: config.AWS_REGION });
|
const sesV2Client = new SESv2Client({ region: config.AWS_REGION });
|
||||||
|
|
||||||
async function sendMagicLinkEmail(email, magicLinkUrl, { isPremium = false } = {}) {
|
async function sendMagicLinkEmail(email, magicLinkUrl, { isPremium = false } = {}) {
|
||||||
@@ -10,21 +12,22 @@ async function sendMagicLinkEmail(email, magicLinkUrl, { isPremium = false } = {
|
|||||||
? ' <span style="font-size: 12px; font-weight: 500; color: #0600fb; letter-spacing: 0.02em;">Premium</span>'
|
? ' <span style="font-size: 12px; font-weight: 500; color: #0600fb; letter-spacing: 0.02em;">Premium</span>'
|
||||||
: '';
|
: '';
|
||||||
const params = {
|
const params = {
|
||||||
Source: config.EMAIL_FROM,
|
FromEmailAddress: config.EMAIL_FROM,
|
||||||
Destination: {
|
Destination: {
|
||||||
ToAddresses: [email],
|
ToAddresses: [email],
|
||||||
},
|
},
|
||||||
Message: {
|
Content: {
|
||||||
Subject: {
|
Simple: {
|
||||||
Data: 'Sign in to RYS',
|
Subject: {
|
||||||
Charset: 'UTF-8',
|
Data: 'Sign in to RYS',
|
||||||
},
|
|
||||||
Body: {
|
|
||||||
Text: {
|
|
||||||
Data: `Here's your sign-in link for RYS:\n\n${magicLinkUrl}\n\nLink expires in 15 minutes. If you didn't request this, just ignore it.\n\n— Lawrence`,
|
|
||||||
Charset: 'UTF-8',
|
Charset: 'UTF-8',
|
||||||
},
|
},
|
||||||
Html: {
|
Body: {
|
||||||
|
Text: {
|
||||||
|
Data: `Here's your sign-in link for RYS:\n\n${magicLinkUrl}\n\nLink expires in 15 minutes. If you didn't request this, just ignore it.\n\n— Lawrence`,
|
||||||
|
Charset: 'UTF-8',
|
||||||
|
},
|
||||||
|
Html: {
|
||||||
Data: `
|
Data: `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@@ -58,33 +61,35 @@ async function sendMagicLinkEmail(email, magicLinkUrl, { isPremium = false } = {
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`.trim(),
|
`.trim(),
|
||||||
Charset: 'UTF-8',
|
Charset: 'UTF-8',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = new SendEmailCommand(params);
|
const command = new SendEmailCommand(params);
|
||||||
return sesClient.send(command);
|
return sesV2Client.send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendWelcomeEmail(email) {
|
async function sendWelcomeEmail(email) {
|
||||||
const params = {
|
const params = {
|
||||||
Source: config.EMAIL_FROM,
|
FromEmailAddress: config.EMAIL_FROM,
|
||||||
Destination: {
|
Destination: {
|
||||||
ToAddresses: [email],
|
ToAddresses: [email],
|
||||||
},
|
},
|
||||||
Message: {
|
Content: {
|
||||||
Subject: {
|
Simple: {
|
||||||
Data: 'Welcome to RYS Premium',
|
Subject: {
|
||||||
Charset: 'UTF-8',
|
Data: 'Welcome to RYS Premium',
|
||||||
},
|
|
||||||
Body: {
|
|
||||||
Text: {
|
|
||||||
Data: `Welcome to RYS Premium!\n\nThank you for subscribing. Your premium features are now active.\n\n— Lawrence`,
|
|
||||||
Charset: 'UTF-8',
|
Charset: 'UTF-8',
|
||||||
},
|
},
|
||||||
Html: {
|
Body: {
|
||||||
|
Text: {
|
||||||
|
Data: `Welcome to RYS Premium!\n\nThank you for subscribing. Your premium features are now active.\n\n— Lawrence`,
|
||||||
|
Charset: 'UTF-8',
|
||||||
|
},
|
||||||
|
Html: {
|
||||||
Data: `
|
Data: `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@@ -112,35 +117,37 @@ async function sendWelcomeEmail(email) {
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`.trim(),
|
`.trim(),
|
||||||
Charset: 'UTF-8',
|
Charset: 'UTF-8',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = new SendEmailCommand(params);
|
const command = new SendEmailCommand(params);
|
||||||
return sesClient.send(command);
|
return sesV2Client.send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FEEDBACK_URL = 'https://docs.google.com/forms/d/1AzQQxTWgG6M5N87jinvXKQkGS6Mehzg19XV4mjteTK0';
|
const FEEDBACK_URL = 'https://docs.google.com/forms/d/1AzQQxTWgG6M5N87jinvXKQkGS6Mehzg19XV4mjteTK0';
|
||||||
|
|
||||||
async function sendCancellationEmail(email) {
|
async function sendCancellationEmail(email) {
|
||||||
const params = {
|
const params = {
|
||||||
Source: config.EMAIL_FROM,
|
FromEmailAddress: config.EMAIL_FROM,
|
||||||
Destination: {
|
Destination: {
|
||||||
ToAddresses: [email],
|
ToAddresses: [email],
|
||||||
},
|
},
|
||||||
Message: {
|
Content: {
|
||||||
Subject: {
|
Simple: {
|
||||||
Data: 'Your RYS Premium subscription has been canceled',
|
Subject: {
|
||||||
Charset: 'UTF-8',
|
Data: 'Your RYS Premium subscription has been canceled',
|
||||||
},
|
|
||||||
Body: {
|
|
||||||
Text: {
|
|
||||||
Data: `Your RYS Premium subscription has been canceled.\n\nThe free version of RYS is still yours to use anytime.\n\nIf you have a moment, we'd love to hear what we could do better: ${FEEDBACK_URL}\n\nThanks for giving Premium a try.\n\n— Lawrence`,
|
|
||||||
Charset: 'UTF-8',
|
Charset: 'UTF-8',
|
||||||
},
|
},
|
||||||
Html: {
|
Body: {
|
||||||
|
Text: {
|
||||||
|
Data: `Your RYS Premium subscription has been canceled.\n\nThe free version of RYS is still yours to use anytime.\n\nIf you have a moment, we'd love to hear what we could do better: ${FEEDBACK_URL}\n\nThanks for giving Premium a try.\n\n— Lawrence`,
|
||||||
|
Charset: 'UTF-8',
|
||||||
|
},
|
||||||
|
Html: {
|
||||||
Data: `
|
Data: `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@@ -180,14 +187,15 @@ async function sendCancellationEmail(email) {
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`.trim(),
|
`.trim(),
|
||||||
Charset: 'UTF-8',
|
Charset: 'UTF-8',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = new SendEmailCommand(params);
|
const command = new SendEmailCommand(params);
|
||||||
return sesClient.send(command);
|
return sesV2Client.send(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateEmail(email) {
|
async function validateEmail(email) {
|
||||||
|
|||||||
Reference in New Issue
Block a user