This commit is contained in:
Leandro Facchinetti 2020-03-18 13:38:09 -04:00
parent bcd9a8efce
commit 56936d209b
2 changed files with 106 additions and 13 deletions

View File

@ -1,4 +1,11 @@
import React from "react"; import React from "react";
import ReactDOMServer from "react-dom/server";
import cryptoRandomString from "crypto-random-string";
export type Inbox = {
name: string;
token: string;
};
export class Layout extends React.Component { export class Layout extends React.Component {
render() { render() {
@ -81,21 +88,23 @@ export class Form extends React.Component {
} }
} }
export class Created extends React.Component<{ name: string; token: string }> { export class Created extends React.Component<{ inbox: Inbox }> {
render() { render() {
const { name, token } = this.props.inbox;
return ( return (
<> <>
<h1>{this.props.name} Inbox Created</h1> <h1>{name} Inbox Created</h1>
<p> <p>
Sign up for the newsletter with Sign up for the newsletter with
<br /> <br />
<code>{this.props.token}@kill-the-newsletter.com</code> <code>{token}@kill-the-newsletter.com</code>
</p> </p>
<p> <p>
Subscribe to the Atom feed at Subscribe to the Atom feed at
<br /> <br />
<code> <code>
https://www.kill-the-newsletter.com/feeds/{this.props.token}.xml https://www.kill-the-newsletter.com/feeds/{token}
.xml
</code> </code>
</p> </p>
<p> <p>
@ -115,3 +124,80 @@ export class Created extends React.Component<{ name: string; token: string }> {
); );
} }
} }
// https://validator.w3.org/feed/docs/atom.html
export function Feed(inbox: Inbox): object {
const { name, token } = inbox;
return {
feed: {
$: { xmlns: "http://www.w3.org/2005/Atom" },
link: [
{
$: {
rel: "self",
type: "application/atom+xml",
href: `https://www.kill-the-newsletter.com/feeds/${token}.xml`
}
},
{
$: {
rel: "alternate",
type: "text/html",
href: "https://www.kill-the-newsletter.com/"
}
}
],
id: id(token),
title: name,
subtitle: `Kill the Newsletter! Inbox “${token}@kill-the-newsletter.com”`,
updated: now(),
...Entry({
title: `“#${name}” Inbox Created`,
author: "Kill the Newsletter!",
content: ReactDOMServer.renderToStaticMarkup(
<Created inbox={inbox}></Created>
)
})
}
};
}
export function Entry({
title,
author,
content
}: {
title: string;
author: string;
content: string;
}): object {
return {
entry: {
id: id(newToken()),
title,
author: { name: author },
updated: now(),
content: { $: { type: "html" }, _: content }
}
};
}
export function newToken(): string {
return cryptoRandomString({
length: 20,
characters: "1234567890qwertyuiopasdfghjklzxcvbnm"
});
}
export function now(): string {
return new Date().toISOString();
}
export function feedPath(token: string): string {
return `static/feeds/${token}.xml`;
}
export function id(token: string): string {
return `urn:kill-the-newsletter:${token}`;
}

View File

@ -1,9 +1,17 @@
import express from "express"; import express from "express";
import React from "react"; import React from "react";
import ReactDOMServer from "react-dom/server"; import ReactDOMServer from "react-dom/server";
import { Layout, Form, Created } from "./components"; import {
Inbox,
Layout,
Form,
Created,
Feed,
newToken,
feedPath
} from "./components";
import { Builder } from "xml2js";
import fs from "fs"; import fs from "fs";
import cryptoRandomString from "crypto-random-string";
const app = express(); const app = express();
@ -21,16 +29,15 @@ app.get("/", (req, res) =>
); );
app.post("/", (req, res) => { app.post("/", (req, res) => {
const inbox: Inbox = { name: req.body.name, token: newToken() };
fs.writeFileSync(
feedPath(inbox.token),
new Builder().buildObject(Feed(inbox))
);
res.send( res.send(
render( render(
<Layout> <Layout>
<Created <Created inbox={inbox}></Created>
name={req.body.name}
token={cryptoRandomString({
length: 20,
characters: "1234567890qwertyuiopasdfghjklzxcvbnm"
})}
></Created>
</Layout> </Layout>
) )
); );