diff --git a/src/components.tsx b/src/components.tsx index 8150a15..67c06b9 100644 --- a/src/components.tsx +++ b/src/components.tsx @@ -1,4 +1,11 @@ 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 { 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() { + const { name, token } = this.props.inbox; return ( <> -

“{this.props.name}” Inbox Created

+

“{name}” Inbox Created

Sign up for the newsletter with
- {this.props.token}@kill-the-newsletter.com + {token}@kill-the-newsletter.com

Subscribe to the Atom feed at
- https://www.kill-the-newsletter.com/feeds/{this.props.token}.xml + https://www.kill-the-newsletter.com/feeds/{token} + .xml

@@ -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( + + ) + }) + } + }; +} + +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}`; +} diff --git a/src/server.tsx b/src/server.tsx index c2cd406..d86f5e6 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -1,9 +1,17 @@ import express from "express"; import React from "react"; 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 cryptoRandomString from "crypto-random-string"; const app = express(); @@ -21,16 +29,15 @@ app.get("/", (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( render( - + ) );