diff --git a/backend/scripts/aws/marketplace/startup_script.sh b/backend/scripts/aws/marketplace/startup_script.sh index f11df3186..345c7735f 100755 --- a/backend/scripts/aws/marketplace/startup_script.sh +++ b/backend/scripts/aws/marketplace/startup_script.sh @@ -64,8 +64,8 @@ sudo -u dshop yarn build:dist cd $BACKEND_PATH truncate -s 0 .env -# Env var indicating it is a AWS deployment. -echo "AWS_MARKETPLACE_DEPLOYMENT=true" >> .env +# Env var indicating it is a AWS deployment. Append this to the env files in both the 'backend' and 'shop' folders +echo "AWS_MARKETPLACE_DEPLOYMENT=true" | tee -a .env ../shop/.env # Encryption key for storing data securely in the DB. echo "ENCRYPTION_KEY=`openssl rand -base64 48`" >> .env diff --git a/backend/utils/const.js b/backend/utils/const.js index 69d3f28ae..b7954a43a 100644 --- a/backend/utils/const.js +++ b/backend/utils/const.js @@ -68,7 +68,8 @@ const { IPFS_GATEWAY, // IPFS gateway override BUCKET_PREFIX = DEFAULT_BUCKET_PREFIX, SERVICE_PREFIX = DEFAULT_SERVICE_PREFIX, - EXTERNAL_IP + EXTERNAL_IP, + AWS_MARKETPLACE_DEPLOYMENT //bool indicating whether the app is running from an AWS EC2 instance launched via the marketplace } = process.env /** @@ -128,6 +129,7 @@ module.exports = { BUCKET_PREFIX, SERVICE_PREFIX, EXTERNAL_IP, + AWS_MARKETPLACE_DEPLOYMENT, EXTERNAL_IP_SERVICE_URL, DEFAULT_INFRA_RESOURCES, DEFAULT_AWS_REGION, diff --git a/backend/utils/emails/_getTransport.js b/backend/utils/emails/_getTransport.js index 9ed5b87a0..28c4d2fdd 100644 --- a/backend/utils/emails/_getTransport.js +++ b/backend/utils/emails/_getTransport.js @@ -1,6 +1,6 @@ const nodemailer = require('nodemailer') const aws = require('aws-sdk') -// const { IS_TEST } = require('../const') +const { AWS_MARKETPLACE_DEPLOYMENT, DEFAULT_AWS_REGION } = require('../const') const encConf = require('../encryptedConfig') function getTransportFromConfig(config) { @@ -24,12 +24,24 @@ function getTransportFromConfig(config) { } }) } else if (config.email === 'aws') { - const SES = new aws.SES({ - apiVersion: '2010-12-01', - region: config.awsRegion, - accessKeyId: config.awsAccessKey, - secretAccessKey: config.awsAccessSecret - }) + let SES + + if (AWS_MARKETPLACE_DEPLOYMENT) { + //Use the credentials from the EC2 Instance metadata + SES = new aws.SES({ + apiVersion: '2010-12-01', + region: DEFAULT_AWS_REGION + }) + } else { + //Look up the shop admin's AWS credentials from 'config' + SES = new aws.SES({ + apiVersion: '2010-12-01', + region: config.awsRegion, + accessKeyId: config.awsAccessKey, + secretAccessKey: config.awsAccessSecret + }) + } + return nodemailer.createTransport({ SES }) } } diff --git a/backend/utils/emails/newOrder.js b/backend/utils/emails/newOrder.js index 81f0d1192..5eaad4bfe 100644 --- a/backend/utils/emails/newOrder.js +++ b/backend/utils/emails/newOrder.js @@ -159,8 +159,8 @@ async function sendNewOrderEmail({ ...varsOverride } - const htmlOutputVendor = mjml2html(vendor(vars), { minify: true }) - const htmlOutput = mjml2html(email(vars), { minify: true }) + const htmlOutputVendor = mjml2html(vendor(vars)) + const htmlOutput = mjml2html(email(vars)) const txtOutput = emailTxt(vars) const message = { diff --git a/backend/utils/emails/passwordReset.js b/backend/utils/emails/passwordReset.js index c3d9dc1f8..d207e1d6e 100644 --- a/backend/utils/emails/passwordReset.js +++ b/backend/utils/emails/passwordReset.js @@ -30,7 +30,7 @@ async function sendPasswordResetEmail({ network, seller, verifyUrl, skip }) { fromEmail: from } - const htmlOutput = mjml2html(forgotPassEmail(vars), { minify: true }) + const htmlOutput = mjml2html(forgotPassEmail(vars)) const txtOutput = forgotPassEmailTxt(vars) const message = { diff --git a/backend/utils/emails/printfulOrderFailed.js b/backend/utils/emails/printfulOrderFailed.js index 1884ada82..1ec10a40d 100644 --- a/backend/utils/emails/printfulOrderFailed.js +++ b/backend/utils/emails/printfulOrderFailed.js @@ -39,7 +39,7 @@ async function sendPrintfulOrderFailedEmail(shopId, orderData, opts, skip) { fromEmail: from } - const htmlOutput = mjml2html(printfulOrderFailed(vars), { minify: true }) + const htmlOutput = mjml2html(printfulOrderFailed(vars)) const txtOutput = printfulOrderFailedTxt(vars) const message = { diff --git a/backend/utils/emails/sellerContact.js b/backend/utils/emails/sellerContact.js index 2af536389..d63e718ff 100644 --- a/backend/utils/emails/sellerContact.js +++ b/backend/utils/emails/sellerContact.js @@ -35,7 +35,7 @@ async function sellerContactEmail({ network, seller, data, skip }) { fromEmail: from } - const htmlOutput = mjml2html(sellerContact(vars), { minify: true }) + const htmlOutput = mjml2html(sellerContact(vars)) const txtOutput = sellerContactTxt(vars) const message = { diff --git a/backend/utils/emails/stripeWebhookError.js b/backend/utils/emails/stripeWebhookError.js index 4e7a554ef..3d42585f8 100644 --- a/backend/utils/emails/stripeWebhookError.js +++ b/backend/utils/emails/stripeWebhookError.js @@ -32,7 +32,7 @@ async function stripeWebhookErrorEmail(shopId, errorData, skip) { ...errorData } - const htmlOutput = mjml2html(stripeWebhookError(vars), { minify: true }) + const htmlOutput = mjml2html(stripeWebhookError(vars)) const txtOutput = stripeWebhookErrorTxt(vars) const message = { diff --git a/backend/utils/emails/verifyEmail.js b/backend/utils/emails/verifyEmail.js index 0797cc80e..8a44976aa 100644 --- a/backend/utils/emails/verifyEmail.js +++ b/backend/utils/emails/verifyEmail.js @@ -24,7 +24,7 @@ async function sendVerifyEmail({ network, seller, verifyUrl, skip }) { const { name, email } = seller const vars = { head, name, verifyUrl, fromEmail: from } - const htmlOutput = mjml2html(verifyEmail(vars), { minify: true }) + const htmlOutput = mjml2html(verifyEmail(vars)) const txtOutput = verifyEmailTxt(vars) const message = { diff --git a/shop/src/pages/admin/settings/Server.js b/shop/src/pages/admin/settings/Server.js index aee59531b..ca45cb66d 100644 --- a/shop/src/pages/admin/settings/Server.js +++ b/shop/src/pages/admin/settings/Server.js @@ -277,7 +277,8 @@ const AdminSettings = ({ shop }) => { )} - {state.email !== 'aws' ? null : ( + {state.email !== 'aws' && + !process.env.AWS_MARKETPLACE_DEPLOYMENT ? null : ( <>
diff --git a/shop/src/pages/admin/settings/apps/List.js b/shop/src/pages/admin/settings/apps/List.js index 132d4d9b1..9af6f9cef 100644 --- a/shop/src/pages/admin/settings/apps/List.js +++ b/shop/src/pages/admin/settings/apps/List.js @@ -2,6 +2,7 @@ import React, { useMemo, useState } from 'react' import fbt, { FbtParam } from 'fbt' import useShopConfig from 'utils/useShopConfig' +import useBackendApi from 'utils/useBackendApi' import useEmailAppsList from 'utils/useEmailAppsList' import maskSecret from 'utils/maskSecret' @@ -19,6 +20,7 @@ const AppSettings = () => { const { shopConfig, refetch } = useShopConfig() const [connectModal, setShowConnectModal] = useState(false) const { emailAppsList } = useEmailAppsList({ shopConfig }) + const { post } = useBackendApi({ authToken: true }) const appsList = useMemo(() => { if (!shopConfig) return [] @@ -69,7 +71,25 @@ const AppSettings = () => { diff --git a/shop/src/pages/super-admin/networks/_Form.js b/shop/src/pages/super-admin/networks/_Form.js index c1310f532..8ac0ff761 100644 --- a/shop/src/pages/super-admin/networks/_Form.js +++ b/shop/src/pages/super-admin/networks/_Form.js @@ -198,6 +198,32 @@ const NetworkForm = ({ onSave, network, feedback, className }) => { shopConfig: state.fallbackShopConfig }) + /* + * The Networks form should ask the shop admin for their AWS Credentials only when the DShop DApp is not deployed on an EC2 instance launched via AWS Marketplace. + * Reason: When DShop is deployed with the help of the Marketplace solution, the shop admin's AWS credentials can be obtained programatically. + * Reference: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#instance-metadata-security-credentials + */ + const conditionallyRequestAWSCreds = () => { + if (process.env.AWS_MARKETPLACE_DEPLOYMENT) { + return + } else { + return ( +
+
+ + + {Feedback('awsAccessKeyId')} +
+
+ + + {Feedback('awsSecretAccessKey')} +
+
+ ) + } + } + const ProcessorIdToEmailComp = { sendgrid: SendgridModal, aws: AWSModal, @@ -229,7 +255,21 @@ const NetworkForm = ({ onSave, network, feedback, className }) => { @@ -244,13 +284,21 @@ const NetworkForm = ({ onSave, network, feedback, className }) => { const selection = Object.keys(newState.infra).filter( (k) => newState.infra[k] ) - const validRes = validateSelection({ networkConfig: newState, selection }) - - if (!validRes.success) { - setState({ ...stateUpdate, infraErrors: validRes.errors }) - return false + const awsResources = ['aws-files', 'aws-cdn', 'aws-dns', 'aws-email'] + + //Validate selection only if it is something other than an AWS Resource, and DShop is not running on Amazon EC2. + //Reason: If DShop is running on EC2, it can connect to an[other] AWS resource automatically + if ( + !awsResources.includes(selection) && + !process.env.AWS_MARKETPLACE_DEPLOYMENT + ) { + const validRes = validateSelection({ networkConfig: newState, selection }) + + if (!validRes.success) { + setState({ ...stateUpdate, infraErrors: validRes.errors }) + return false + } } - setState({ ...stateUpdate, infraErrors: [] }) return true } @@ -408,18 +456,7 @@ const NetworkForm = ({ onSave, network, feedback, className }) => { {Feedback('gcpCredentials')}
-
-
- - - {Feedback('awsAccessKeyId')} -
-
- - - {Feedback('awsSecretAccessKey')} -
-
+ {conditionallyRequestAWSCreds()}
diff --git a/shop/src/utils/useEmailAppsList.js b/shop/src/utils/useEmailAppsList.js index 8a8fb06e6..27da08bf3 100644 --- a/shop/src/utils/useEmailAppsList.js +++ b/shop/src/utils/useEmailAppsList.js @@ -33,9 +33,10 @@ const useEmailAppsList = ({ shopConfig }) => { { id: 'aws', title: 'AWS SES', - description: awsEnabled - ? `AWS SES Access Key: ${maskSecret(awsAccessKey, 12)}` - : 'Send emails using AWS SES', + description: + awsEnabled && !process.env.AWS_MARKETPLACE_DEPLOYMENT + ? `AWS SES Access Key: ${maskSecret(awsAccessKey, 12)}` + : 'Send emails using AWS SES', icon: , enabled: awsEnabled }, diff --git a/shop/webpack.config.js b/shop/webpack.config.js index e8418ab24..c324245db 100644 --- a/shop/webpack.config.js +++ b/shop/webpack.config.js @@ -224,7 +224,8 @@ const webpackConfig = { DATA_DIR: process.env.DATA_DIR || '', CONTENT_CDN: process.env.CONTENT_CDN || '', CONTENT_HASH: process.env.CONTENT_HASH || '', - ABSOLUTE: process.env.ABSOLUTE || '' + ABSOLUTE: process.env.ABSOLUTE || '', + AWS_MARKETPLACE_DEPLOYMENT: null //use null by default. Otherwise, use the value for process.env.AWS_MARKETPLACE_DEPLOYMENT }), new MiniCssExtractPlugin({ filename: '[name].[contenthash:8].css' }) ],