From 5b070efc4f7bb0be8938a83dcdf5ace4faba69ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20N=C3=BDvlt?= Date: Fri, 1 Mar 2024 10:21:55 +0100 Subject: [PATCH] Add sending script --- scripts/send-confirmation-reminder.js | 152 ++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 scripts/send-confirmation-reminder.js diff --git a/scripts/send-confirmation-reminder.js b/scripts/send-confirmation-reminder.js new file mode 100644 index 0000000..0365514 --- /dev/null +++ b/scripts/send-confirmation-reminder.js @@ -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 `, + to: sub.email, + htmlBody: ` + + + + + ${title} + + + +
+

Last chance to verify e-mail

+

You have recently submitted message to Vienna Insurance Group, which is about to be sent to CEOs soon.

+

We just need to confirm your e-mail to make sure it really exists.

+

Please click following button to confirm your e-mail:

+

Confirm e-mail

+
+
+ + +`, + }; + console.log("values", values); + // mailClient.send(values +}