Files
temetro/backend/src/lib/email.ts
T
Claude 9dabe2f5d2 Add TypeScript + Express + Postgres backend with Better Auth
Implements the first temetro backend: an Express 5 API on Postgres via
Drizzle ORM, with authentication and multi-tenant clinics powered by
Better Auth.

- Auth: email/password with required email verification, password reset,
  rate limiting, CSRF/trusted-origins, secure cookies, session audit hook.
- Organizations (clinics) with RBAC (owner/admin/member/viewer) and an
  extended `patient` permission set; member invitations by email.
- Org-scoped patient records mirroring the frontend Patient shape, with
  CRUD endpoints gated by permission (read/write/delete).
- Email helper logs links to the console when SMTP is unset (zero-setup
  local dev); Dockerfile + docker-compose (db + backend + frontend) with
  migrations applied on startup and a configurable Postgres host port.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 21:27:32 +03:00

50 lines
1.2 KiB
TypeScript

import nodemailer from "nodemailer";
import { env } from "../env.js";
type SendArgs = {
to: string;
subject: string;
text: string;
html?: string;
};
// Lazily build a transport. With SMTP_HOST configured we send real mail;
// otherwise we fall back to logging the message (and any links) to the
// server console — zero setup for local / open-source development.
const transport = env.SMTP_HOST
? nodemailer.createTransport({
host: env.SMTP_HOST,
port: env.SMTP_PORT ?? 587,
secure: (env.SMTP_PORT ?? 587) === 465,
auth:
env.SMTP_USER && env.SMTP_PASS
? { user: env.SMTP_USER, pass: env.SMTP_PASS }
: undefined,
})
: null;
export async function sendEmail({ to, subject, text, html }: SendArgs): Promise<void> {
if (!transport) {
console.info(
[
"",
"✉️ [email:console] No SMTP configured — printing instead of sending.",
` to: ${to}`,
` subject: ${subject}`,
` body: ${text}`,
"",
].join("\n"),
);
return;
}
await transport.sendMail({
from: env.SMTP_FROM,
to,
subject,
text,
html: html ?? text,
});
}