diff --git a/src/index.tsx b/src/index.tsx
index 9f701ed..f511ac8 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -23,13 +23,16 @@ const webApp = express()
)
.post("/", (req, res) => {
const name = req.body.name;
- const token = newToken();
- fs.writeFileSync(feedPath(token), renderXML(Feed({ name, token })));
+ const identifier = newIdentifier();
+ fs.writeFileSync(
+ feedPath(identifier),
+ renderXML(Feed({ name, identifier }))
+ );
res.send(
renderHTML(
“{name}” Inbox Created
-
+
)
);
@@ -41,8 +44,8 @@ const emailApp: SMTPServerOptions = {
const paths = session.envelope.rcptTo.flatMap(({ address }) => {
const match = address.match(/^(\w+)@kill-the-newsletter.com$/);
if (match === null) return [];
- const token = match[1];
- const path = feedPath(token);
+ const identifier = match[1];
+ const path = feedPath(identifier);
if (!fs.existsSync(path)) return [];
return [path];
});
@@ -179,23 +182,23 @@ function Form() {
);
}
-function Created({ token }: { token: string }) {
+function Created({ identifier }: { identifier: string }) {
return (
<>
Sign up for the newsletter with
- {feedEmail(token)}
+ {feedEmail(identifier)}
Subscribe to the Atom feed at
- {feedURL(token)}
+ {feedURL(identifier)}
Don’t share these addresses.
- They contain a security token that other people could use
+ They contain an identifier that other people could use
to send you spam and to control your newsletter subscriptions.
@@ -209,7 +212,7 @@ function Created({ token }: { token: string }) {
);
}
-function Feed({ name, token }: { name: string; token: string }) {
+function Feed({ name, identifier }: { name: string; identifier: string }) {
return {
feed: {
$: { xmlns: "http://www.w3.org/2005/Atom" },
@@ -218,7 +221,7 @@ function Feed({ name, token }: { name: string; token: string }) {
$: {
rel: "self",
type: "application/atom+xml",
- href: feedURL(token)
+ href: feedURL(identifier)
}
},
{
@@ -229,15 +232,17 @@ function Feed({ name, token }: { name: string; token: string }) {
}
}
],
- id: id(token),
+ id: urn(identifier),
title: name,
- subtitle: `Kill the Newsletter! Inbox “${feedEmail(token)}”`,
+ subtitle: `Kill the Newsletter! Inbox: ${feedEmail(
+ identifier
+ )} → ${feedURL(identifier)}`,
updated: now(),
...Entry({
title: `“${name}” Inbox Created`,
author: "Kill the Newsletter!",
content: ReactDOMServer.renderToStaticMarkup(
-
+
)
})
}
@@ -255,7 +260,7 @@ function Entry({
}) {
return {
entry: {
- id: id(newToken()),
+ id: urn(newIdentifier()),
title,
author: { name: author },
updated: now(),
@@ -264,7 +269,7 @@ function Entry({
};
}
-function newToken(): string {
+function newIdentifier(): string {
return cryptoRandomString({
length: 20,
characters: "1234567890qwertyuiopasdfghjklzxcvbnm"
@@ -275,20 +280,20 @@ function now(): string {
return new Date().toISOString();
}
-function feedPath(token: string): string {
- return `static/feeds/${token}.xml`;
+function feedPath(identifier: string): string {
+ return `static/feeds/${identifier}.xml`;
}
-function feedURL(token: string): string {
- return `https://www.kill-the-newsletter.com/feeds/${token}.xml`;
+function feedURL(identifier: string): string {
+ return `https://www.kill-the-newsletter.com/feeds/${identifier}.xml`;
}
-export function feedEmail(token: string): string {
- return `${token}@kill-the-newsletter.com`;
+export function feedEmail(identifier: string): string {
+ return `${identifier}@kill-the-newsletter.com`;
}
-function id(token: string): string {
- return `urn:kill-the-newsletter:${token}`;
+function urn(identifier: string): string {
+ return `urn:kill-the-newsletter:${identifier}`;
}
function renderHTML(component: React.ReactElement): string {
diff --git a/src/test.ts b/src/test.ts
index efb8041..9288746 100644
--- a/src/test.ts
+++ b/src/test.ts
@@ -4,9 +4,9 @@ import axios from "axios";
import qs from "qs";
test("create feed", async () => {
- const token = await createFeed();
+ const identifier = await createFeed();
- expect(await readFeed(token)).toMatch("My Feed");
+ expect(await readFeed(identifier)).toMatch("My Feed");
});
describe("receive email", () => {
@@ -17,41 +17,41 @@ describe("receive email", () => {
});
test("HTML content", async () => {
- const token = await createFeed();
+ const identifier = await createFeed();
await transporter.sendMail({
from: "publisher@example.com",
- to: feedEmail(token),
+ to: feedEmail(identifier),
subject: "New Message",
html: "HTML content
"
});
- const feed = await readFeed(token);
+ const feed = await readFeed(identifier);
expect(feed).toMatch("publisher@example.com");
expect(feed).toMatch("New Message");
expect(feed).toMatch("HTML content");
});
test("text content", async () => {
- const token = await createFeed();
+ const identifier = await createFeed();
await transporter.sendMail({
from: "publisher@example.com",
- to: feedEmail(token),
+ to: feedEmail(identifier),
subject: "New Message",
text: "TEXT content"
});
- const feed = await readFeed(token);
+ const feed = await readFeed(identifier);
expect(feed).toMatch("TEXT content");
});
test("truncation", async () => {
- const token = await createFeed();
+ const identifier = await createFeed();
for (const repetition of [...new Array(4).keys()])
await transporter.sendMail({
from: "publisher@example.com",
- to: feedEmail(token),
+ to: feedEmail(identifier),
subject: "New Message",
text: `REPETITION ${repetition} `.repeat(10_000)
});
- const feed = await readFeed(token);
+ const feed = await readFeed(identifier);
expect(feed).toMatch("REPETITION 3");
expect(feed).not.toMatch("REPETITION 0");
}, 10_000);
@@ -74,6 +74,7 @@ async function createFeed(): Promise {
).data.match(/(\w{20}).xml/)![1];
}
-async function readFeed(token: string): Promise {
- return (await axios.get(`http://localhost:8000/feeds/${token}.xml`)).data;
+async function readFeed(identifier: string): Promise {
+ return (await axios.get(`http://localhost:8000/feeds/${identifier}.xml`))
+ .data;
}