This commit is contained in:
parent
bcd9a8efce
commit
56936d209b
|
@ -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}`;
|
||||||
|
}
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue