fix: show live ticker across app and website and suppress review emails
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* Clean Code: Single Responsibility Principle
|
||||
*/
|
||||
|
||||
import nodemailer from 'nodemailer'
|
||||
import { createMailTransport, shouldSuppressMail } from './mail-transport.js'
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import { getServerDataPath } from './paths.js'
|
||||
@@ -103,7 +103,7 @@ async function getEmailRecipients(data, config) {
|
||||
* Creates email transporter
|
||||
* @returns {Object} Nodemailer transporter
|
||||
*/
|
||||
function createTransporter() {
|
||||
function createTransporter(event) {
|
||||
const smtpUser = process.env.SMTP_USER
|
||||
const smtpPass = process.env.SMTP_PASS
|
||||
|
||||
@@ -114,7 +114,7 @@ function createTransporter() {
|
||||
)
|
||||
}
|
||||
|
||||
return nodemailer.createTransport({
|
||||
return createMailTransport(event, {
|
||||
host: process.env.SMTP_HOST || 'localhost',
|
||||
port: parseInt(process.env.SMTP_PORT) || 587,
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
@@ -131,13 +131,14 @@ function createTransporter() {
|
||||
* @param {string} pdfPath - Path to generated PDF
|
||||
* @returns {Promise<Object>} Email result
|
||||
*/
|
||||
export async function sendMembershipEmail(data, pdfPath) {
|
||||
export async function sendMembershipEmail(data, pdfPath, event) {
|
||||
try {
|
||||
if (await shouldSuppressMail(event, [data.email])) return { success: true, suppressed: true, recipients: [], message: 'Testmodus: E-Mail-Versand unterdrückt.' }
|
||||
const config = await loadConfig()
|
||||
const recipients = await getEmailRecipients(data, config)
|
||||
|
||||
// Create transporter
|
||||
const transporter = createTransporter()
|
||||
const transporter = createTransporter(event)
|
||||
|
||||
// Email content
|
||||
const subject = `Neuer Mitgliedschaftsantrag - ${data.vorname} ${data.nachname}`
|
||||
@@ -187,13 +188,14 @@ Das ausgefüllte Formular ist als Anhang verfügbar.`
|
||||
* Sends a simple registration notification to Vorstand/admin and a confirmation to user.
|
||||
* @param {Object} data - { name, email, phone }
|
||||
*/
|
||||
export async function sendRegistrationNotification(data) {
|
||||
export async function sendRegistrationNotification(data, event) {
|
||||
try {
|
||||
if (await shouldSuppressMail(event, [data.email])) return { success: true, suppressed: true, recipients: [], message: 'Testmodus: E-Mail-Versand unterdrückt.' }
|
||||
const config = await loadConfig()
|
||||
const recipients = await getEmailRecipients(data, config)
|
||||
|
||||
// Create transporter
|
||||
const transporter = createTransporter()
|
||||
const transporter = createTransporter(event)
|
||||
|
||||
// Notify Vorstand/admin
|
||||
const adminSubject = 'Neue Registrierung - Harheimer TC'
|
||||
|
||||
50
server/utils/mail-transport.js
Normal file
50
server/utils/mail-transport.js
Normal file
@@ -0,0 +1,50 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user