fix(notifier): don't construct Resend without an API key (CI crash)

lib/notifier.ts eagerly did `new Resend(process.env.RESEND_API_KEY)` whenever
NODE_ENV !== "development". Resend v4's constructor throws on a missing key, so
in any env without RESEND_API_KEY (CI, non-dev test runs) merely importing the
module crashed — surfaced by crew-records.test.ts once Phase 4c pulled
requisition-service → notifier into the crew actions' import graph.

Construct the client only when a key is present; otherwise fall back to console
logging (the send branches now gate on `!resend` instead of `isDev`). Verified by
running the full integration suite with RESEND_API_KEY unset (195 pass).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hardik 2026-06-22 21:41:36 +05:30
parent 4e71863c57
commit 712e040fc2

View file

@ -3,7 +3,10 @@ import { db } from "@/lib/db";
import type { PurchaseOrder, User } from "@prisma/client";
const isDev = process.env.NODE_ENV === "development";
const resend = isDev ? null : new Resend(process.env.RESEND_API_KEY);
// Construct the Resend client only when a key is actually present — in dev, CI,
// or any env without RESEND_API_KEY we fall back to console logging (the Resend
// v4 constructor throws on a missing key). `canSend` gates the real send path.
const resend = !isDev && process.env.RESEND_API_KEY ? new Resend(process.env.RESEND_API_KEY) : null;
const FROM = `${process.env.EMAIL_FROM_NAME ?? "PPMS"} <${process.env.EMAIL_FROM ?? "noreply@ppms.pelagiamarine.com"}>`;
const APP_URL = (process.env.NEXTAUTH_URL ?? "https://portal.pelagiamarine.com").replace(/\/$/, "");
@ -84,13 +87,13 @@ export async function notify({ event, po, recipients, note }: NotifyParams) {
const link = buildInAppLink(event, po, recipient);
let status = "sent";
if (isDev) {
if (!resend) {
console.log(
`\n📧 [DEV EMAIL] To: ${recipient.email}\n Subject: ${subject}\n Body: ${buildEmailBody(event, po, note)}\n Link: ${APP_URL}${link}\n`
);
} else {
try {
const { error } = await resend!.emails.send({
const { error } = await resend.emails.send({
from: FROM,
to: recipient.email,
subject,
@ -441,13 +444,13 @@ export async function notifyCrew({ event, recipients, subject, body, link }: Cre
await Promise.allSettled(
recipients.map(async (recipient) => {
let status = "sent";
if (isDev) {
if (!resend) {
console.log(
`\n📧 [DEV EMAIL] To: ${recipient.email}\n Subject: ${subject}\n Body: ${body}\n Link: ${APP_URL}${link ?? ""}\n`
);
} else {
try {
const { error } = await resend!.emails.send({
const { error } = await resend.emails.send({
from: FROM,
to: recipient.email,
subject,