Email, SMS & Uploads

Handlebars-templated transactional email, the SMS hook point, and ImageKit upload auth.

Mailer (transactional email)

lib/mailer.ts (class Mailer) wraps nodemailer with a Handlebars template registry. SMTP transport is configured from the SMTP_* env vars.

import { Mailer } from "@plugins/Huruf-Tech/thunder-core/lib/mailer.ts";

// Register a named template (lazy fetcher; cached after first render).
Mailer.addTemplate("welcome", () => ({
  subject: "Welcome {{name}}",
  html: "<h1>Hi {{name}}</h1>",
}));

// Send using a template (extra fields are passed to Handlebars), or raw html/text.
await Mailer.send({ template: "welcome", to: "a@b.com", name: "Alice" });
await Mailer.send({ to: "a@b.com", subject: "Hi", html: "<p>...</p>" });

A _blank template id is used as a fallback when a requested template isn't registered. thunder-core itself uses the Mailer for password-reset and magic-link emails.

Nodemailer timeouts are 15s connect, 15s greeting and 30s socket; set SMTP_DEBUG for debug output.

Bypassing SMTP

Set Mailer.onSendMail to route the rendered payload to your own provider. Templates still render - nodemailer is never touched:

Mailer.onSendMail = async (payload) => {
  // payload: { from, to, subject, html, text }
  await myProvider.send(payload);
};

routes/tenantInvites.ts swallows mail errors on invite creation, so an invite is still created when SMTP is down. Check your mail provider, not the API response, when invites appear not to send.

SMS

lib/sms.ts has no built-in provider. It is a hook point: assign SMS.onSendSms at boot, or SMS.send throws "SMS sending is not configured."

import { SMS } from "@plugins/Huruf-Tech/thunder-core/lib/sms.ts";

SMS.onSendSms = async ({ to, text }) => {
  await myGateway.send(to, text);
};

await SMS.send({ to: "+10000000000", text: "Your code is 123456" });

The phone-number verification endpoints on routes/me.ts depend on this - they will not work until SMS.onSendSms is configured. See Routes & Schemas.

ImageKit upload auth

routes/imagekit.ts exposes an endpoint that returns short-lived ImageKit client-side upload credentials, using IMAGEKIT_PRIVATE_KEY / IMAGEKIT_PUBLIC_KEY.

Read routes/imagekit.ts for the exact path and response shape. Your frontend uses these credentials to upload directly to ImageKit without proxying files through your server.


On this page