This commit is contained in:
parent
93ec567562
commit
6799b5fa42
137
src/index.ts
137
src/index.ts
|
@ -28,81 +28,25 @@ export default function killTheNewsletter(
|
||||||
const database = new Database(
|
const database = new Database(
|
||||||
path.join(rootDirectory, "kill-the-newsletter.db")
|
path.join(rootDirectory, "kill-the-newsletter.db")
|
||||||
);
|
);
|
||||||
database.function("newRandomReference", (): string =>
|
|
||||||
cryptoRandomString({
|
|
||||||
length: 16,
|
|
||||||
characters: "abcdefghijklmnopqrstuvwxyz0123456789",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
database.function(
|
|
||||||
"welcomeEntryTitle",
|
|
||||||
(title: string): string => `“${title}” inbox created`
|
|
||||||
);
|
|
||||||
database.function(
|
|
||||||
"welcomeEntryContent",
|
|
||||||
(feedReference: string): HTML => html`
|
|
||||||
<p>
|
|
||||||
Sign up for the newsletter with<br />
|
|
||||||
<code class="copyable"
|
|
||||||
>${feedReference}@${webApplication.get("email host")}</code
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Subscribe to the Atom feed at<br />
|
|
||||||
<code class="copyable"
|
|
||||||
>${webApplication.get("url")}/feeds/${feedReference}.xml</code
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<strong>Don’t share these addresses.</strong><br />
|
|
||||||
They contain an identifier that other people could use to send you spam
|
|
||||||
and to control your newsletter subscriptions.
|
|
||||||
</p>
|
|
||||||
<p><strong>Enjoy your readings!</strong></p>
|
|
||||||
<p>
|
|
||||||
<a href="${webApplication.get("url")}/"
|
|
||||||
><strong>Create another inbox</strong></a
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
);
|
|
||||||
databaseMigrate(database, [
|
databaseMigrate(database, [
|
||||||
sql`
|
sql`
|
||||||
CREATE TABLE "feeds" (
|
CREATE TABLE "feeds" (
|
||||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
"createdAt" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createdAt" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
"updatedAt" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"updatedAt" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
"reference" TEXT NOT NULL UNIQUE DEFAULT (newRandomReference()),
|
"reference" TEXT NOT NULL UNIQUE,
|
||||||
"title" TEXT NOT NULL
|
"title" TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE "entries" (
|
CREATE TABLE "entries" (
|
||||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
"createdAt" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createdAt" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
"reference" TEXT NOT NULL UNIQUE DEFAULT (newRandomReference()),
|
"reference" TEXT NOT NULL UNIQUE,
|
||||||
"feed" INTEGER NOT NULL REFERENCES "feeds",
|
"feed" INTEGER NOT NULL REFERENCES "feeds",
|
||||||
"title" TEXT NOT NULL,
|
"title" TEXT NOT NULL,
|
||||||
"author" TEXT NOT NULL,
|
"author" TEXT NOT NULL,
|
||||||
"content" TEXT NOT NULL
|
"content" TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TRIGGER "welcomeEntry"
|
|
||||||
AFTER INSERT ON "feeds"
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO "entries" ("feed", "title", "author", "content")
|
|
||||||
VALUES (
|
|
||||||
"NEW"."id",
|
|
||||||
welcomeEntryTitle("NEW"."title"),
|
|
||||||
'Kill the Newsletter!',
|
|
||||||
welcomeEntryContent("NEW"."reference")
|
|
||||||
);
|
|
||||||
END;
|
|
||||||
|
|
||||||
CREATE TRIGGER "entriesInsertImpliesFeedsUpdatedAt"
|
|
||||||
AFTER INSERT ON "entries"
|
|
||||||
BEGIN
|
|
||||||
UPDATE "feeds" SET "updatedAt" = CURRENT_TIMESTAMP WHERE "id" = "NEW"."feed";
|
|
||||||
END;
|
|
||||||
`,
|
`,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -323,26 +267,67 @@ export default function killTheNewsletter(
|
||||||
)
|
)
|
||||||
return res.status(400).send(
|
return res.status(400).send(
|
||||||
layout(
|
layout(
|
||||||
html`<p>
|
html`
|
||||||
|
<p>
|
||||||
Error: Missing newsletter name.
|
Error: Missing newsletter name.
|
||||||
<a href="${webApplication.get("url")}/"
|
<a href="${webApplication.get("url")}/"
|
||||||
><strong>Try again</strong></a
|
><strong>Try again</strong></a
|
||||||
>.
|
>.
|
||||||
</p>`
|
</p>
|
||||||
|
`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const feedReference = newReference();
|
||||||
|
const welcomeTitle = `“${req.body.name}” inbox created`;
|
||||||
|
const welcomeContent = html`
|
||||||
|
<p>
|
||||||
|
Sign up for the newsletter with<br />
|
||||||
|
<code class="copyable"
|
||||||
|
>${feedReference}@${webApplication.get("email host")}</code
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Subscribe to the Atom feed at<br />
|
||||||
|
<code class="copyable"
|
||||||
|
>${webApplication.get("url")}/feeds/${feedReference}.xml</code
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Don’t share these addresses.</strong><br />
|
||||||
|
They contain an identifier that other people could use to send you spam
|
||||||
|
and to control your newsletter subscriptions.
|
||||||
|
</p>
|
||||||
|
<p><strong>Enjoy your readings!</strong></p>
|
||||||
|
<p>
|
||||||
|
<a href="${webApplication.get("url")}/"
|
||||||
|
><strong>Create another inbox</strong></a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
database.executeTransaction(() => {
|
||||||
const feedId = database.run(
|
const feedId = database.run(
|
||||||
sql`INSERT INTO "feeds" ("title") VALUES (${req.body.name})`
|
sql`INSERT INTO "feeds" ("reference", "title") VALUES (${feedReference}, ${req.body.name})`
|
||||||
).lastInsertRowid;
|
).lastInsertRowid;
|
||||||
const entry = database.get<{ title: string; content: HTML }>(
|
database.run(
|
||||||
sql`SELECT "title", "content" FROM "entries" WHERE "feed" = ${feedId}`
|
sql`
|
||||||
)!;
|
INSERT INTO "entries" ("reference", "feed", "title", "author", "content")
|
||||||
|
VALUES (
|
||||||
|
${newReference()}
|
||||||
|
${feedId},
|
||||||
|
${welcomeTitle},
|
||||||
|
'Kill the Newsletter!',
|
||||||
|
${welcomeContent}
|
||||||
|
)
|
||||||
|
`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
res.send(
|
res.send(
|
||||||
layout(html`
|
layout(html`
|
||||||
<p><strong>${entry.title}</strong></p>
|
<p><strong>${welcomeTitle}</strong></p>
|
||||||
$${entry.content}
|
$${welcomeContent}
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -475,10 +460,19 @@ export default function killTheNewsletter(
|
||||||
if (feed === undefined) continue;
|
if (feed === undefined) continue;
|
||||||
database.run(
|
database.run(
|
||||||
sql`
|
sql`
|
||||||
INSERT INTO "entries" ("feed", "title", "author", "content")
|
INSERT INTO "entries" ("reference", "feed", "title", "author", "content")
|
||||||
VALUES (${feed.id}, ${subject}, ${from}, ${body})
|
VALUES (
|
||||||
|
${newReference()},
|
||||||
|
${feed.id},
|
||||||
|
${subject},
|
||||||
|
${from},
|
||||||
|
${body}
|
||||||
|
)
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
database.run(
|
||||||
|
sql`UPDATE "feeds" SET "updatedAt" = CURRENT_TIMESTAMP WHERE "id" = ${feed.id}`
|
||||||
|
);
|
||||||
while (renderFeed(feedReference)!.length > 500_000)
|
while (renderFeed(feedReference)!.length > 500_000)
|
||||||
database.run(
|
database.run(
|
||||||
sql`DELETE FROM "entries" WHERE "feed" = ${feed.id} ORDER BY "createdAt" ASC LIMIT 1`
|
sql`DELETE FROM "entries" WHERE "feed" = ${feed.id} ORDER BY "createdAt" ASC LIMIT 1`
|
||||||
|
@ -497,6 +491,13 @@ export default function killTheNewsletter(
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function newReference(): string {
|
||||||
|
return cryptoRandomString({
|
||||||
|
length: 16,
|
||||||
|
characters: "abcdefghijklmnopqrstuvwxyz0123456789",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return { webApplication, emailApplication };
|
return { webApplication, emailApplication };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue