This commit is contained in:
mdares
2026-01-06 15:47:19 +00:00
parent ea92b32618
commit 05a30b2a21
4 changed files with 97 additions and 8 deletions

7
app/api/health/route.ts Normal file
View File

@@ -0,0 +1,7 @@
import { NextResponse } from "next/server";
import { logLine } from "@/lib/logger";
export async function GET() {
logLine("health.hit", { ok: true });
return NextResponse.json({ ok: true });
}

View File

@@ -5,6 +5,8 @@ import { prisma } from "@/lib/prisma";
import { DEFAULT_ALERTS, DEFAULT_DEFAULTS, DEFAULT_SHIFT } from "@/lib/settings";
import { buildVerifyEmail, sendEmail } from "@/lib/email";
import { getBaseUrl } from "@/lib/appUrl";
import { logLine } from "@/lib/logger";
function slugify(input: string) {
const trimmed = input.trim().toLowerCase();
@@ -120,10 +122,16 @@ export async function POST(req: Request) {
text: emailContent.text,
html: emailContent.html,
});
} catch {
} catch (err: any) {
emailSent = false;
logLine("signup.verify_email.failed", {
email,
message: err?.message,
code: err?.code,
response: err?.response,
responseCode: err?.responseCode,
});
}
return NextResponse.json({
ok: true,
verificationRequired: true,

View File

@@ -1,4 +1,6 @@
import nodemailer from "nodemailer";
import { logLine } from "@/lib/logger";
type EmailPayload = {
to: string;
@@ -25,11 +27,23 @@ function getTransporter() {
throw new Error("SMTP not configured");
}
const smtpDebug = process.env.SMTP_DEBUG === "true";
logLine("smtp.config", {
host,
port,
secure,
user,
from: process.env.SMTP_FROM,
smtpDebug,
});
cachedTransport = nodemailer.createTransport({
host,
port,
secure,
auth: { user, pass },
logger: smtpDebug,
debug: smtpDebug,
});
return cachedTransport;
@@ -40,15 +54,55 @@ export async function sendEmail(payload: EmailPayload) {
if (!from) {
throw new Error("SMTP_FROM not configured");
}
logLine("email.send.start", {
to: payload.to,
subject: payload.subject,
from,
});
const transporter = getTransporter();
return transporter.sendMail({
try {
const info = await transporter.sendMail({
from,
to: payload.to,
subject: payload.subject,
text: payload.text,
html: payload.html,
headers: {
"X-Mailer": "MIS Control Tower",
},
replyTo: from,
});
// Nodemailer response details:
logLine("email.send.ok", {
to: payload.to,
from,
messageId: info.messageId,
response: info.response,
accepted: info.accepted,
rejected: info.rejected,
pending: (info as any).pending,
});
return info;
} catch (err: any) {
logLine("email.send.err", {
to: payload.to,
from,
name: err?.name,
message: err?.message,
code: err?.code,
command: err?.command,
response: err?.response,
responseCode: err?.responseCode,
stack: err?.stack,
});
throw err;
}
}
export function buildVerifyEmail(params: { appName: string; verifyUrl: string }) {

20
lib/logger.ts Normal file
View File

@@ -0,0 +1,20 @@
import fs from "fs";
import path from "path";
const LOG_PATH = process.env.LOG_FILE || "/tmp/mis-control-tower.log";
export function logLine(event: string, data: Record<string, any> = {}) {
const line = JSON.stringify({
ts: new Date().toISOString(),
event,
...data,
});
console.log(line);
try {
fs.mkdirSync(path.dirname(LOG_PATH), { recursive: true });
fs.appendFileSync(LOG_PATH, line + "\n", { encoding: "utf8" });
} catch {
// If file logging fails, we still want something:
console.error("[logLine-failed]", line);
}
}