Initial commit: Port Nimara CRM (Layers 0-4)
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled

Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 11:52:51 +01:00
commit 67d7e6e3d5
572 changed files with 86496 additions and 0 deletions

57
src/lib/email/index.ts Normal file
View File

@@ -0,0 +1,57 @@
import nodemailer, { type Transporter } from 'nodemailer';
import { env } from '@/lib/env';
import { logger } from '@/lib/logger';
/**
* Creates and returns a new Nodemailer SMTP transporter.
*
* A new instance is created on each call so the factory can be used in
* contexts where connection pooling is managed externally (e.g. per-request
* in serverless, or once at worker startup).
*/
export function createTransporter(): Transporter {
return nodemailer.createTransport({
host: env.SMTP_HOST,
port: env.SMTP_PORT,
// Implicitly secure when port is 465; STARTTLS for all other ports.
secure: env.SMTP_PORT === 465,
});
}
export interface SendEmailOptions {
to: string | string[];
subject: string;
html: string;
from?: string;
}
/**
* Sends a single email via SMTP.
*
* Returns the nodemailer info object on success. Propagates errors to the
* caller — callers in background jobs should wrap in try/catch and handle
* retries via BullMQ.
*/
export async function sendEmail(
to: string | string[],
subject: string,
html: string,
from?: string,
): Promise<nodemailer.SentMessageInfo> {
const transporter = createTransporter();
const info = await transporter.sendMail({
from: from ?? `Port Nimara CRM <noreply@${env.SMTP_HOST}>`,
to: Array.isArray(to) ? to.join(', ') : to,
subject,
html,
});
logger.debug(
{ messageId: info.messageId, to, subject },
'Email sent',
);
return info;
}