This commit is contained in:
Leandro Facchinetti 2020-03-18 22:41:40 -04:00
parent abaf038520
commit a838bac66f
4 changed files with 61 additions and 9 deletions

44
package-lock.json generated
View File

@ -887,6 +887,15 @@
} }
} }
}, },
"@types/nodemailer": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.0.tgz",
"integrity": "sha512-KY7bFWB0MahRZvVW4CuW83qcCDny59pJJ0MQ5ifvfcjNwPlIT0vW4uARO4u1gtkYnWdhSvURegecY/tzcukJcA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/prop-types": { "@types/prop-types": {
"version": "15.7.3", "version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
@ -928,6 +937,16 @@
"@types/mime": "*" "@types/mime": "*"
} }
}, },
"@types/smtp-server": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@types/smtp-server/-/smtp-server-3.5.3.tgz",
"integrity": "sha512-15g2q1C2F3iiVOQlEjChndNb9wAZZiNBLIO0Xg4+JGc9FvdiD1p6DaLVkbU1I8eHd0g624xz0ateamo3nY0ENw==",
"dev": true,
"requires": {
"@types/node": "*",
"@types/nodemailer": "*"
}
},
"@types/stack-utils": { "@types/stack-utils": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
@ -1320,6 +1339,11 @@
} }
} }
}, },
"base32.js": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz",
"integrity": "sha1-tYLexpPC8R6JPPBk7mrFthMaIgI="
},
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@ -2866,6 +2890,11 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
}, },
"ipv6-normalize": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ipv6-normalize/-/ipv6-normalize-1.0.1.tgz",
"integrity": "sha1-GzJYKQ02X6gyOeiZB93kWS52IKg="
},
"is-accessor-descriptor": { "is-accessor-descriptor": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@ -4924,6 +4953,11 @@
} }
} }
}, },
"nodemailer": {
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.5.tgz",
"integrity": "sha512-NH7aNVQyZLAvGr2+EOto7znvz+qJ02Cb/xpou98ApUt5tEAUSVUxhvHvgV/8I5dhjKTYqUw0nasoKzLNBJKrDQ=="
},
"nodemon": { "nodemon": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz",
@ -6051,6 +6085,16 @@
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
"dev": true "dev": true
}, },
"smtp-server": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/smtp-server/-/smtp-server-3.6.0.tgz",
"integrity": "sha512-DVEVWzL4s1GWzAs4+6rbhNZpAn61+V8l4b7R8zHLAW2jmlwKz9IKQmdgm5sNruCRnS01BYyitI98vJo7LDnXfg==",
"requires": {
"base32.js": "0.1.0",
"ipv6-normalize": "1.0.1",
"nodemailer": "6.4.5"
}
},
"snapdragon": { "snapdragon": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",

View File

@ -8,6 +8,7 @@
"express": "^4.17.1", "express": "^4.17.1",
"react": "^16.13.0", "react": "^16.13.0",
"react-dom": "^16.13.0", "react-dom": "^16.13.0",
"smtp-server": "^3.6.0",
"xml2js": "^0.4.23" "xml2js": "^0.4.23"
}, },
"devDependencies": { "devDependencies": {
@ -17,6 +18,7 @@
"@types/node-fetch": "^2.5.5", "@types/node-fetch": "^2.5.5",
"@types/react": "^16.9.23", "@types/react": "^16.9.23",
"@types/react-dom": "^16.9.5", "@types/react-dom": "^16.9.5",
"@types/smtp-server": "^3.5.3",
"@types/xml2js": "^0.4.5", "@types/xml2js": "^0.4.5",
"concurrently": "^5.1.0", "concurrently": "^5.1.0",
"jest": "^25.1.0", "jest": "^25.1.0",

View File

@ -1,5 +1,6 @@
import express from "express"; import express from "express";
import { Server } from "http"; import { Server } from "http";
import { SMTPServer } from "smtp-server";
import React from "react"; import React from "react";
import ReactDOMServer from "react-dom/server"; import ReactDOMServer from "react-dom/server";
import { Builder } from "xml2js"; import { Builder } from "xml2js";
@ -11,7 +12,7 @@ const webApp = express()
.use(express.urlencoded({ extended: true })) .use(express.urlencoded({ extended: true }))
.get("/", (req, res) => .get("/", (req, res) =>
res.send( res.send(
renderHtml( renderHTML(
<Layout> <Layout>
<Form></Form> <Form></Form>
</Layout> </Layout>
@ -20,9 +21,9 @@ const webApp = express()
) )
.post("/", (req, res) => { .post("/", (req, res) => {
const inbox: Inbox = { name: req.body.name, token: newToken() }; const inbox: Inbox = { name: req.body.name, token: newToken() };
fs.writeFileSync(feedPath(inbox.token), renderXml(Feed(inbox))); fs.writeFileSync(feedPath(inbox.token), renderXML(Feed(inbox)));
res.send( res.send(
renderHtml( renderHTML(
<Layout> <Layout>
<Created inbox={inbox}></Created> <Created inbox={inbox}></Created>
</Layout> </Layout>
@ -30,6 +31,8 @@ const webApp = express()
); );
}); });
export const emailServer = new SMTPServer();
export let developmentWebServer: Server; export let developmentWebServer: Server;
if (process.env.NODE_ENV === "production") { if (process.env.NODE_ENV === "production") {
@ -48,8 +51,10 @@ if (process.env.NODE_ENV === "production") {
.use(webApp); .use(webApp);
productionWebApp.listen(80); productionWebApp.listen(80);
productionWebApp.listen(443); productionWebApp.listen(443);
emailServer.listen(25);
} else { } else {
developmentWebServer = webApp.listen(8000); developmentWebServer = webApp.listen(8000);
emailServer.listen(2525);
} }
type Inbox = { type Inbox = {
@ -144,7 +149,7 @@ function Created({ inbox: { name, token } }: { inbox: Inbox }) {
<p> <p>
Subscribe to the Atom feed at Subscribe to the Atom feed at
<br /> <br />
<code>{feedUrl(token)}</code> <code>{feedURL(token)}</code>
</p> </p>
<p> <p>
Dont share these addresses. Dont share these addresses.
@ -176,7 +181,7 @@ function Feed(inbox: Inbox) {
$: { $: {
rel: "self", rel: "self",
type: "application/atom+xml", type: "application/atom+xml",
href: feedUrl(token) href: feedURL(token)
} }
}, },
{ {
@ -237,7 +242,7 @@ export function feedPath(token: string) {
return `static/feeds/${token}.xml`; return `static/feeds/${token}.xml`;
} }
function feedUrl(token: string) { function feedURL(token: string) {
return `https://www.kill-the-newsletter.com/feeds/${token}.xml`; return `https://www.kill-the-newsletter.com/feeds/${token}.xml`;
} }
@ -249,10 +254,10 @@ function id(token: string) {
return `urn:kill-the-newsletter:${token}`; return `urn:kill-the-newsletter:${token}`;
} }
function renderHtml(component: React.ReactElement): string { function renderHTML(component: React.ReactElement): string {
return `<!DOCTYPE html>\n${ReactDOMServer.renderToStaticMarkup(component)}`; return `<!DOCTYPE html>\n${ReactDOMServer.renderToStaticMarkup(component)}`;
} }
function renderXml(xml: object): string { function renderXML(xml: object): string {
return new Builder().buildObject(xml); return new Builder().buildObject(xml);
} }

View File

@ -1,4 +1,4 @@
import { developmentWebServer, feedPath } from "."; import { developmentWebServer, emailServer, feedPath } from ".";
import fetch from "node-fetch"; import fetch from "node-fetch";
import fs from "fs"; import fs from "fs";
@ -16,4 +16,5 @@ test("create feed", async () => {
afterAll(() => { afterAll(() => {
developmentWebServer.close(); developmentWebServer.close();
emailServer.close(() => {});
}); });