Socket
Socket
Sign inDemoInstall

@netlify/plugin-emails

Package Overview
Dependencies
8
Maintainers
20
Versions
41
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.1 to 1.0.2

./lib/index.js

22

lib/handler/index.d.ts

@@ -6,2 +6,7 @@ import { Handler } from "@netlify/functions";

} | undefined;
interface IAttachment {
content: string;
filename: string;
type: string;
}
export interface IEmailRequest {

@@ -14,2 +19,3 @@ from: string;

bcc?: string;
attachments?: IAttachment[];
}

@@ -26,3 +32,19 @@ interface IEmailConfig {

}
export interface IMissingConfig {
NETLIFY_EMAILS_SECRET?: boolean;
NETLIFY_EMAILS_PROVIDER?: boolean;
NETLIFY_EMAILS_PROVIDER_API_KEY?: boolean;
NETLIFY_EMAILS_MAILGUN_HOST_REGION?: boolean;
NETLIFY_EMAILS_MAILGUN_DOMAIN?: boolean;
}
export interface IRenderRequest {
template: string;
siteId: string;
type: string;
showParameterDictionary: boolean;
parameters: {
[key: string]: string | string[];
};
}
declare const handler: Handler;
export { handler };

496

lib/handler/index.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -41,2 +18,3 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

const path_1 = require("path");
const https_1 = __importDefault(require("https"));
const getEmailFromPath = (path) => {

@@ -62,24 +40,103 @@ let fileFound;

const allowedPreviewEnvironments = ["deploy-preview", "branch-deploy", "dev"];
const isConfigValid = () => {
if (process.env.NETLIFY_EMAILS_PROVIDER_API_KEY === undefined) {
return false;
const getMissingConfig = () => {
var _a;
const missingConfig = {};
let validConfig = true;
if (!process.env.NETLIFY_EMAILS_PROVIDER) {
missingConfig.NETLIFY_EMAILS_PROVIDER = true;
validConfig = false;
}
if (process.env.NETLIFY_EMAILS_PROVIDER === undefined) {
return false;
if (!process.env.NETLIFY_EMAILS_PROVIDER_API_KEY) {
missingConfig.NETLIFY_EMAILS_PROVIDER_API_KEY = true;
validConfig = false;
}
if (process.env.NETLIFY_EMAILS_PROVIDER === "mailgun") {
if (process.env.NETLIFY_EMAILS_MAILGUN_DOMAIN === undefined) {
return false;
if (((_a = process.env.NETLIFY_EMAILS_PROVIDER) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "mailgun") {
if (!process.env.NETLIFY_EMAILS_MAILGUN_HOST_REGION) {
missingConfig.NETLIFY_EMAILS_MAILGUN_HOST_REGION = true;
validConfig = false;
}
if (process.env.NETLIFY_EMAILS_MAILGUN_HOST_REGION === undefined) {
return false;
if (!process.env.NETLIFY_EMAILS_MAILGUN_DOMAIN) {
missingConfig.NETLIFY_EMAILS_MAILGUN_DOMAIN = true;
validConfig = false;
}
}
return true;
if (!process.env.NETLIFY_EMAILS_SECRET) {
missingConfig.NETLIFY_EMAILS_SECRET = true;
validConfig = false;
}
return validConfig ? false : missingConfig;
};
const makeRenderTemplateRequest = (fileFound, parameters) => __awaiter(void 0, void 0, void 0, function* () {
const renderRequest = {
template: fileFound.file,
siteId: process.env.SITE_ID,
type: fileFound.type,
showParameterDictionary: false,
parameters,
};
return yield new Promise((resolve, reject) => {
const renderReq = https_1.default.request({
hostname: "netlify-integration-emails.netlify.app",
path: "/.netlify/functions/render",
method: "POST",
headers: {
"site-id": process.env.SITE_ID,
"Content-Type": "application/json",
},
}, (res) => {
let data = "";
res.on("data", (chunk) => {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
data += chunk;
});
res.on("end", () => {
var _a;
const response = JSON.parse(data);
resolve(Object.assign(Object.assign({}, response), { status: (_a = res.statusCode) !== null && _a !== void 0 ? _a : 500 }));
});
});
renderReq.on("error", (error) => {
return reject(error);
});
renderReq.write(JSON.stringify(renderRequest));
renderReq.end();
});
});
const makeSendEmailRequest = (mailRequest) => __awaiter(void 0, void 0, void 0, function* () {
return yield new Promise((resolve, reject) => {
const sendReq = https_1.default.request({
hostname: "netlify-integration-emails.netlify.app",
path: "/.netlify/functions/send",
method: "POST",
headers: {
"site-id": process.env.SITE_ID,
"Content-Type": "application/json",
},
}, (res) => {
let data = "";
res.on("data", (chunk) => {
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
data += chunk;
});
res.on("end", () => {
var _a;
const response = JSON.parse(data);
const sendEmailResponse = {
message: response.message,
status: (_a = res.statusCode) !== null && _a !== void 0 ? _a : 500,
};
resolve(sendEmailResponse);
});
});
sendReq.on("error", (error) => {
return reject(error);
});
sendReq.write(JSON.stringify(mailRequest));
sendReq.end();
});
});
const handler = (event) => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b, _c;
const { default: fetch } = yield Promise.resolve().then(() => __importStar(require("node-fetch")));
var _a, _b, _c, _d;
console.log(`Email handler received email request from path ${event.rawUrl}`);
const validConfig = isConfigValid();
const missingConfig = getMissingConfig();
const providerApiKey = process.env.NETLIFY_EMAILS_PROVIDER_API_KEY;

@@ -90,53 +147,111 @@ const providerName = process.env.NETLIFY_EMAILS_PROVIDER;

// If missing configuration, render preview HTML and sending missing configuration object to window varialbe
if (!validConfig) {
if (missingConfig) {
const missingConfigString = Object.keys(missingConfig)
.map((key) => {
if (missingConfig[key]) {
return key;
}
return "";
})
.join(", ");
console.error(`Email handler detected missing configuration: ${missingConfigString}`);
if (event.httpMethod === "POST") {
return {
statusCode: 400,
body: JSON.stringify({
message: `The emails integration is not configured correctly. We have detected the following configuration is missing: ${missingConfigString}`,
}),
};
}
return {
statusCode: 200,
body: `
<html>
<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<script>
hljs.highlightAll();
</script>
<link rel="stylesheet" href="https://netlify-integration-emails.netlify.app/main.css">
<script>
config = ${JSON.stringify({
NETLIFY_EMAILS_PROVIDER: providerName,
NETLIFY_EMAILS_PROVIDER_API_KEY: providerApiKey,
NETLIFY_EMAILS_MAILGUN_DOMAIN: process.env.NETLIFY_EMAILS_MAILGUN_DOMAIN,
NETLIFY_EMAILS_MAILGUN_HOST_REGION: process.env.NETLIFY_EMAILS_MAILGUN_HOST_REGION,
})}
siteId = ${JSON.stringify(process.env.SITE_ID)}
templateName = ${JSON.stringify(emailPath)}
</script>
<script defer src='https://netlify-integration-emails.netlify.app/index.js'></script>
</head>
<div id='app'></div>
</html>
`,
<html>
<head>
<link rel="stylesheet" href="https://netlify-integration-emails.netlify.app/main.css">
<script>
missingConfig = ${JSON.stringify(missingConfig)}
siteId = ${JSON.stringify(process.env.SITE_ID)}
templateName = ${JSON.stringify(emailPath)}
</script>
<script defer src='https://netlify-integration-emails.netlify.app/index.js'></script>
</head>
<div id='app'></div>
</html>
`,
};
}
const showEmailPreview = allowedPreviewEnvironments.includes(process.env.CONTEXT);
if (event.httpMethod === "GET" && showEmailPreview) {
if (event.httpMethod === "GET") {
const showEmailPreview = allowedPreviewEnvironments.includes(process.env.CONTEXT);
if (!showEmailPreview) {
return {
statusCode: 400,
body: JSON.stringify({
message: `Email previews are not allowed in the ${process.env.CONTEXT} environment`,
}),
headers: {
"Content-Type": "text/plain",
},
};
}
if (!fs_1.default.existsSync(emailTemplatesDirectory)) {
return {
statusCode: 400,
body: JSON.stringify({
message: `Email templates directory ${emailTemplatesDirectory} does not exist`,
}),
headers: {
"Content-Type": "text/plain",
},
};
}
let emailTemplate;
if (emailPath !== undefined) {
// Return error if preview path is not a valid email path
if (!fs_1.default.existsSync((0, path_1.join)(emailTemplatesDirectory, emailPath))) {
console.log(`Preview path is not a valid email path - preview path received: ${emailPath}`);
return {
statusCode: 400,
body: JSON.stringify({
message: `Preview path is not a valid email path - preview path received: ${emailPath}`,
}),
statusCode: 200,
body: `
<html>
<head>
<link rel="stylesheet" href="https://netlify-integration-emails.netlify.app/main.css">
<script>
missingTemplate = ${JSON.stringify(true)}
siteId = ${JSON.stringify(process.env.SITE_ID)}
templateName = ${JSON.stringify(emailPath)}
emailDirectory = ${JSON.stringify(emailTemplatesDirectory)}
</script>
<script defer src='https://netlify-integration-emails.netlify.app/index.js'></script>
</head>
<div id='app'></div>
</html>
`,
headers: {
"Content-Type": "text/html",
},
};
}
emailTemplate = (0, exports.getEmailFromPath)((0, path_1.join)(emailTemplatesDirectory, emailPath));
// If no email template found, return error
if (emailTemplate === undefined) {
if (!emailTemplate) {
console.log(`No email template found for preview path - preview path received: ${emailPath}. Please ensure that an index.mjml or index.html file exists in the email template folder.`);
return {
statusCode: 400,
body: JSON.stringify({
message: `No email template found for preview path - preview path received: ${emailPath}`,
}),
statusCode: 200,
body: `
<html>
<head>
<link rel="stylesheet" href="https://netlify-integration-emails.netlify.app/main.css">
<script>
missingTemplate = ${JSON.stringify(true)}
siteId = ${JSON.stringify(process.env.SITE_ID)}
templateName = ${JSON.stringify(emailPath)}
emailDirectory = ${JSON.stringify(emailTemplatesDirectory)}
</script>
<script defer src='https://netlify-integration-emails.netlify.app/index.js'></script>
</head>
<div id='app'></div>
</html>
`,
headers: {
"Content-Type": "text/html",
},
};

@@ -147,3 +262,2 @@ }

fs_1.default.readdirSync(emailTemplatesDirectory).forEach((template) => {
// If index.html or index.mjml exists inside template folder, add to validEmailPaths
if (fs_1.default.existsSync((0, path_1.join)(emailTemplatesDirectory, template, "index.html")) ||

@@ -159,6 +273,2 @@ fs_1.default.existsSync((0, path_1.join)(emailTemplatesDirectory, template, "index.mjml"))) {

<head>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<script>
hljs.highlightAll();
</script>
<link rel="stylesheet" href="https://netlify-integration-emails.netlify.app/main.css">

@@ -170,8 +280,6 @@ <script>

siteId = ${JSON.stringify(process.env.SITE_ID)}
config = ${JSON.stringify({
NETLIFY_EMAILS_PROVIDER: providerName,
NETLIFY_EMAILS_PROVIDER_API_KEY: providerApiKey,
NETLIFY_EMAILS_MAILGUN_DOMAIN: process.env.NETLIFY_EMAILS_MAILGUN_DOMAIN,
NETLIFY_EMAILS_MAILGUN_HOST_REGION: process.env.NETLIFY_EMAILS_MAILGUN_HOST_REGION,
})}
siteName = ${JSON.stringify(process.env.SITE_NAME)}
provider = ${JSON.stringify(providerName)}
emailDirectory = ${JSON.stringify(emailTemplatesDirectory)}
secret = ${JSON.stringify(process.env.NETLIFY_EMAILS_SECRET)}
url = ${JSON.stringify(process.env.URL)}

@@ -190,112 +298,130 @@ templateName = ${JSON.stringify(emailPath)}

}
if (process.env.NETLIFY_EMAILS_SECRET === undefined ||
event.headers["netlify-emails-secret"] !== process.env.NETLIFY_EMAILS_SECRET) {
console.log("No secret provided or secret does not match");
return {
statusCode: 403,
body: JSON.stringify({
message: "Request forbidden",
}),
if (event.httpMethod === "POST") {
if (!process.env.NETLIFY_EMAILS_SECRET) {
return {
statusCode: 400,
body: JSON.stringify({
message: "Secret not set in NETLIFY_EMAILS_SECRET",
}),
};
}
if (event.headers["netlify-emails-secret"] !==
process.env.NETLIFY_EMAILS_SECRET) {
return {
statusCode: 403,
body: JSON.stringify({
message: "Secret does not match",
}),
};
}
// If the email templates directory does not exist, return error
if (!fs_1.default.existsSync(emailTemplatesDirectory)) {
return {
statusCode: 404,
body: JSON.stringify({
message: `Email templates directory ${emailTemplatesDirectory} does not exist`,
}),
};
}
if (!event.body) {
return {
statusCode: 400,
body: JSON.stringify({
message: "Request body required",
}),
};
}
const requestBody = JSON.parse(event.body);
if (!requestBody.from) {
console.log("From address is required");
return {
statusCode: 400,
body: JSON.stringify({
message: "From address is required",
}),
};
}
if (!requestBody.to) {
console.log("To address is required");
return {
statusCode: 400,
body: JSON.stringify({
message: "To address is required",
}),
};
}
if (!emailPath) {
console.error(`Email path is not specified`);
return {
statusCode: 400,
body: JSON.stringify({
message: "You have not specified the email you wish to send in the URL",
}),
};
}
const fullEmailPath = `${emailTemplatesDirectory}/${emailPath}`;
const emailPathExists = fs_1.default.existsSync(fullEmailPath);
if (!emailPathExists) {
console.error(`Email path does not exist: ${fullEmailPath}`);
return {
statusCode: 404,
body: JSON.stringify({
message: `Email path ${fullEmailPath} does not exist`,
}),
};
}
const email = (0, exports.getEmailFromPath)(fullEmailPath);
if (!email) {
console.error(`No email file found in directory: ${fullEmailPath}`);
return {
statusCode: 404,
body: JSON.stringify({
message: `No email file found in directory: ${fullEmailPath}`,
}),
};
}
const renderResponseJson = yield makeRenderTemplateRequest(email, requestBody.parameters);
if ((_c = renderResponseJson.error) !== null && _c !== void 0 ? _c : !renderResponseJson.html) {
console.error(`Error rendering email template: ${JSON.stringify(renderResponseJson)}`);
return {
statusCode: renderResponseJson.status,
body: JSON.stringify({
message: `Error rendering email template${renderResponseJson.error ? `: ${renderResponseJson.error}` : ""}`,
}),
};
}
const renderedTemplate = renderResponseJson.html;
const configuration = {
providerName,
apiKey: providerApiKey,
mailgunDomain: process.env.NETLIFY_EMAILS_MAILGUN_DOMAIN,
mailgunHostRegion: process.env.NETLIFY_EMAILS_MAILGUN_HOST_REGION,
};
}
if (event.body === null) {
return {
statusCode: 400,
body: JSON.stringify({
message: "Body required",
}),
const request = {
from: requestBody.from,
to: requestBody.to,
cc: requestBody.cc,
bcc: requestBody.bcc,
subject: (_d = requestBody.subject) !== null && _d !== void 0 ? _d : "",
html: renderedTemplate,
attachments: requestBody.attachments,
};
}
const requestBody = JSON.parse(event.body);
if (requestBody.from === undefined) {
console.log("From address is required");
const { message, status } = yield makeSendEmailRequest({
configuration,
request,
});
if (status !== 200) {
console.error(`Error sending email: ${message}`);
}
return {
statusCode: 400,
statusCode: status,
body: JSON.stringify({
message: "From address is required",
message,
}),
};
}
if (requestBody.to === undefined) {
console.log("To address is required");
return {
statusCode: 400,
body: JSON.stringify({
message: "To address is required",
}),
};
}
if (emailPath === undefined) {
console.error(`Email path is undefined`);
return {
statusCode: 404,
body: JSON.stringify({
message: `Email path is undefined`,
}),
};
}
const fullEmailPath = `${emailTemplatesDirectory}/${emailPath}`;
const directoryExists = fs_1.default.existsSync(fullEmailPath);
if (!directoryExists) {
console.error(`Email directory does not exist: ${fullEmailPath}`);
return {
statusCode: 404,
body: JSON.stringify({
message: `Email path ${fullEmailPath} does not exist`,
}),
};
}
const email = (0, exports.getEmailFromPath)(fullEmailPath);
if (email === undefined) {
console.error(`No email file found in directory: ${fullEmailPath}`);
return {
statusCode: 404,
body: JSON.stringify({
message: `No email file found in directory: ${fullEmailPath}`,
}),
};
}
const renderResponse = yield fetch("https://netlify-integration-emails.netlify.app/.netlify/functions/render?showParamaterDictionary=true", {
method: "POST",
headers: {
"site-id": process.env.SITE_ID,
},
body: JSON.stringify({
template: email.file,
type: email.type,
parameters: requestBody.parameters,
showParamatersDictionary: false,
}),
});
const renderResponseJson = (yield renderResponse.json());
const renderedTemplate = renderResponseJson.html;
const response = yield fetch("https://test-netlify-integration-emails.netlify.app/.netlify/functions/send", {
method: "POST",
headers: {
"site-id": process.env.SITE_ID,
"content-type": "application/json",
},
body: JSON.stringify({
configuration: {
providerName,
apiKey: providerApiKey,
mailgunDomain: process.env.NETLIFY_EMAILS_MAILGUN_DOMAIN,
mailgunHostRegion: process.env.NETLIFY_EMAILS_MAILGUN_HOST_REGION,
},
request: {
from: requestBody.from,
to: requestBody.to,
cc: requestBody.cc,
bcc: requestBody.bcc,
subject: (_c = requestBody.subject) !== null && _c !== void 0 ? _c : "",
html: renderedTemplate,
attachments: requestBody.attachments,
},
}),
});
const responseText = yield response.text();
return {
statusCode: response.status,
statusCode: 405,
body: JSON.stringify({
message: responseText,
message: "Method not allowed",
}),

@@ -302,0 +428,0 @@ };

@@ -1,2 +0,2 @@

export declare const onPreBuild: ({ netlifyConfig, }: {
export declare const onBuild: ({ netlifyConfig, }: {
netlifyConfig: {

@@ -6,3 +6,2 @@ functions: {

included_files: string[];
external_node_modules: string[];
};

@@ -9,0 +8,0 @@ };

@@ -6,7 +6,6 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.onPreBuild = void 0;
// import { execSync } from "child_process";
exports.onBuild = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = require("path");
const onPreBuild = ({ netlifyConfig, }) => {
const onBuild = ({ netlifyConfig, }) => {
var _a, _b, _c;

@@ -18,15 +17,4 @@ const emailsDirectory = (_a = process.env.NETLIFY_EMAILS_DIRECTORY) !== null && _a !== void 0 ? _a : "./emails";

else {
netlifyConfig.functions.emails = Object.assign(Object.assign({}, netlifyConfig.functions.emails), { included_files: [`${emailsDirectory}/**`], external_node_modules: ["node-fetch@2"] });
netlifyConfig.functions.emails = Object.assign(Object.assign({}, netlifyConfig.functions.emails), { included_files: [`${emailsDirectory}/**`] });
}
// const functionDependencies = ["node-fetch@2"];
// // Detect whether project is using NPM, Yarn or Pnpm
// let packageManager = "npm";
// if (fs.existsSync("yarn.lock")) {
// packageManager = "yarn";
// } else if (fs.existsSync("pnpm-lock.yaml")) {
// packageManager = "pnpm";
// }
// console.log("Installing email function dependencies");
// execSync(`${packageManager} install ${functionDependencies.join(" ")} -D`);
// console.log("Installed email function dependencies");
const emailFunctionDirectory = (0, path_1.join)(".netlify", "functions-internal", "emails");

@@ -38,2 +26,2 @@ fs_1.default.mkdirSync(emailFunctionDirectory, {

};
exports.onPreBuild = onPreBuild;
exports.onBuild = onBuild;
{
"name": "@netlify/plugin-emails",
"version": "1.0.1",
"version": "1.0.2",
"description": "A build plugin that creates an email handler and processes requests to send emails",

@@ -18,8 +18,2 @@ "main": "./lib/index.js",

},
"scripts": {
"build": "tsc --project tsconfig.build.json",
"watch": "tsc --project tsconfig.build.json --watch",
"test": "vitest -c ./src/vitest.config.ts --watch=false",
"coverage": "vitest run --coverage -c ./src/vitest.config.ts"
},
"keywords": [

@@ -57,3 +51,9 @@ "emails",

"vitest": "^0.25.8"
},
"scripts": {
"build": "tsc --project tsconfig.build.json",
"watch": "tsc --project tsconfig.build.json --watch",
"test": "vitest -c ./src/vitest.config.ts --watch=false",
"coverage": "vitest run --coverage -c ./src/vitest.config.ts"
}
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc