Add sending script

This commit is contained in:
Ondřej 2024-03-01 10:21:55 +01:00
parent d5cf8d5a26
commit 5b070efc4f

View file

@ -0,0 +1,152 @@
import { Kysely, SqliteDialect, sql } from "kysely";
import Database from "better-sqlite3";
import { z } from "zod";
import { snakeCase } from "change-case";
import { join } from "node:path";
import "dotenv/config";
// --- DB
const path = process.env.DATABASE_PATH;
if (!path) {
throw new Error("DATABASE_PATH environment variable is not defined.");
}
console.log("Database path", path);
const database = new Database(path);
database.exec("PRAGMA journal_mode = WAL;");
const dialect = new SqliteDialect({ database });
const db = new Kysely({ dialect });
// --- email client
const responseSchema = z.object({
status: z.union([z.literal("success"), z.literal("error")]),
data: z.record(z.any()),
});
function convertObjectKeys(obj, fn) {
const entries = Object.entries(obj).map(([key, val]) => [fn(key), val]);
return Object.fromEntries(entries);
}
class PostalError extends Error {
constructor(code, message) {
super(message);
this.name = code;
}
}
export function createClient({ host, key }) {
return {
async send(config) {
const url = join(host, "/api/v1/send/message");
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Server-API-Key": key,
},
body: JSON.stringify(convertObjectKeys(config, snakeCase)),
});
const data = await res.json();
const parsed = responseSchema.parse(data);
if (parsed.status === "error") {
throw new PostalError(parsed.data.code, parsed.data.message);
}
return parsed.data;
},
};
}
const host = process.env.POSTAL_HOST ?? "https://resend.cz";
const key = process.env.POSTAL_KEY;
if (!key) {
throw new Error("Postal API key not defined");
}
export const mailClient = createClient({ host, key });
// -----
const unconfirmedSubmissions = await db
.selectFrom("letters")
.select(["language", "email", "confirmationToken"])
.where("confirmed", "=", 0)
.execute();
console.log("Unconfirmed submissions", unconfirmedSubmissions.length);
for (const sub of unconfirmedSubmissions) {
const title = "Last chance to verify email";
const url = new URL(`${sub.language}/confirm/${sub.confirmationToken}`, process.env.PUBLIC_URL);
const values = {
subject: title,
from: `Let's stop dirty money <vig@re-set.cz>`,
to: sub.email,
htmlBody: `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>${title}</title>
<style>
body {
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
.button {
display: inline-block;
padding: 1em 2em;
background-color: #a71122;
color: white;
text-decoration: underline;
min-width: 150px;
text-align: center;
}
h1 {
font-weight: bold;
font-size: 1.5em;
}
p {
font
}
.rule {
border-bottom: 1px solid #ddd;
}
.separator {
color: #ddd;
margin-left: 0.25em;
margin-right: 0.25em;
}
.body {
font-size: 1.25em;
}
</style>
</head>
<body>
<div class="body">
<h1>Last chance to verify e-mail</h1>
<p>You have recently submitted message to Vienna Insurance Group, which is about to be sent to CEOs soon.</p>
<p>We just need to confirm your e-mail to make sure it really exists.</p>
<p>Please click following button to confirm your e-mail:</p>
<p><a class="button" href="${url}">Confirm e-mail</a></p>
</div>
<div class="rule"></div>
<p class="footer">
<strong>Re-set</strong><span class="separator">·</span>
<a href="https://re-set.cz/about-us" target="_blank">About</a><span class="separator">·</span>
<a href="https://www.facebook.com/PlatformaReset" target="_blank">Facebook</a><span class="separator">·</span>
<a href="https://www.instagram.com/reset_platforma/">Instagram</a><span class="separator">·</span>
<a href="https://twitter.com/reset_platforma" target="_blank">X</a>
</p>
</body>
</html>`,
};
console.log("values", values);
// mailClient.send(values
}