import nodemailer from 'nodemailer' import * as auth from './auth.js' const normalize = value => String(value || '').trim().toLowerCase() const enabled = value => ['1', 'true', 'yes', 'on'].includes(normalize(value)) export function isReviewUser(user) { if (!user) return false const email = normalize(user.email) return normalize(user.accountType) === 'playstore_review' || Boolean(email && email === normalize(process.env.PLAYSTORE_REVIEW_EMAIL)) } function addresses(values) { return values.flat(Infinity).filter(Boolean).flatMap(value => { if (typeof value === 'object') return [normalize(value.address)] return String(value).split(',').map(part => normalize(part.match(/<([^>]+)>/)?.[1] || part)) }) } export async function shouldSuppressMail(event, emails = []) { if (enabled(process.env.MAIL_DELIVERY_DISABLED)) return true // Use the authenticated account, never a client-provided demo flag. if (event) { const token = auth.getTokenFromEvent(event) if (token && isReviewUser(await auth.getUserFromToken(token))) return true } const targets = addresses(emails) if (!targets.length) return false if (targets.includes(normalize(process.env.PLAYSTORE_REVIEW_EMAIL))) return true const users = await auth.readUsers() return users.some(user => isReviewUser(user) && targets.includes(normalize(user.email))) } // All SMTP entry points go through this gate, including newsletter batches. // Create the real transport lazily so suppressed mail cannot open a connection. export function createMailTransport(event, options) { let transport return { async sendMail(mail) { if (await shouldSuppressMail(event, [mail.to, mail.cc, mail.bcc, mail.replyTo])) { return { suppressed: true, messageId: null, accepted: [], rejected: [] } } transport ||= nodemailer.createTransport(options) return transport.sendMail(mail) } } }