Files
wondif_vue/server/utils/mailer.js
2026-06-18 22:05:43 +02:00

388 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import nodemailer from "nodemailer"
const transporters = {}
function isSmtpConfigured(config) {
return Boolean(
config.host &&
config.port &&
config.user &&
config.password &&
config.fromEmail
)
}
function getSmtpConfig(config, suffix) {
const key = suffix.charAt(0).toUpperCase() + suffix.slice(1)
return {
host: config[`smtpHost${key}`],
port: config[`smtpPort${key}`],
secure: config[`smtpSecure${key}`],
user: config[`smtpUser${key}`],
password: config[`smtpPassword${key}`],
fromEmail: config[`smtpFromEmail${key}`],
fromName: config[`smtpFromName${key}`],
}
}
function getFrom(config) {
return config.fromName
? `"${config.fromName}" <${config.fromEmail}>`
: config.fromEmail
}
function getTransporter(suffix) {
if (transporters[suffix]) {
return transporters[suffix]
}
const config = getSmtpConfig(useRuntimeConfig(), suffix)
if (!isSmtpConfigured(config)) {
return null
}
transporters[suffix] = nodemailer.createTransport({
host: config.host,
port: Number(config.port),
secure: String(config.secure).toLowerCase() === "true",
connectionTimeout: 10000,
greetingTimeout: 10000,
socketTimeout: 15000,
auth: {
user: config.user,
pass: config.password,
},
})
return transporters[suffix]
}
function escapeHtml(value) {
return String(value)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;")
}
export async function sendParcDemandEmails(payload) {
const config = useRuntimeConfig()
const smtpConfig = getSmtpConfig(config, "parc")
const mailer = getTransporter("parc")
if (!mailer || !config.parcRequestRecipientEmail) {
return false
}
const from = getFrom(smtpConfig)
const adminSubject = `WONDIF - Formulaire de demande Parc instrumental - ${payload.name}`
const adminText = [
"Une nouvelle demande a été envoyée depuis le formulaire du Parc instrumental.",
"",
`Type de demande : ${payload.requestType}`,
`Nom : ${payload.name}`,
`Email : ${payload.email}`,
`Telephone : ${payload.phone}`,
`Structure : ${payload.organization}`,
"",
"Message :",
payload.message,
].join("\n")
const userSubject = "ONDIF - Confirmation de votre demande - Parc instrumental"
const userText = [
`Bonjour ${payload.name},`,
"",
"Votre demande a bien été transmise à l'équipe du Parc instrumental.",
"Nous reviendrons vers vous dans les meilleurs délais.",
"",
"Recapitulatif :",
`Type de demande : ${payload.requestType}`,
`Structure : ${payload.organization}`,
`Telephone : ${payload.phone}`,
"",
"Votre message :",
payload.message,
].join("\n")
await Promise.all([
mailer.sendMail({
from,
to: config.parcRequestRecipientEmail,
replyTo: payload.email,
subject: adminSubject,
text: adminText,
}),
mailer.sendMail({
from,
to: payload.email,
subject: userSubject,
text: userText,
}),
])
return true
}
function formatProjetLyceePriority(priority) {
if (priority === null || priority === undefined) return "Non renseigné"
if (priority === 0) return "Non souhaité"
return `Priorité ${priority}`
}
function getProjetLyceeSummary(payload) {
const projectWishes = [
["Concert-rencontre", payload.concertRencontrePriority],
["Parcours Entrer dans la peau d'un professionnel du spectacle", payload.immersionPriority],
["Parcours EAC", payload.eacPriority],
["Concert commente", payload.concertCommentePriority],
]
.filter(([, priority]) => priority >= 1 && priority <= 4)
.map(([label, priority]) => `${label} : ${formatProjetLyceePriority(priority)}`)
return [
`Etablissement scolaire : ${payload.schoolName}`,
`Departement : ${payload.department}`,
`Ville : ${payload.city}`,
`Enseignant coordinateur : ${payload.coordinatorName}`,
`Email : ${payload.email}`,
`Telephone : ${payload.phone}`,
`Discipline enseignee : ${payload.discipline}`,
`Niveau de classe : ${payload.classLevel}`,
`Nombre probable d'eleves concernes : ${payload.studentCount}`,
...(projectWishes.length ? ["", "Souhaits de projet :", ...projectWishes] : []),
"",
"Periode souhaitée :",
payload.projectPeriod,
].join("\n")
}
export async function sendProjetLyceeEmails(payload) {
const config = useRuntimeConfig()
const smtpConfig = getSmtpConfig(config, "aec")
const mailer = getTransporter("aec")
if (!mailer) {
console.warn("Email projet lycée non envoyé: configuration SMTP incomplète.")
return false
}
if (!config.scolaireRequestRecipientEmail) {
console.warn("Email projet lycée non envoyé: destinataire scolaire manquant.")
return false
}
const from = getFrom(smtpConfig)
const summary = getProjetLyceeSummary(payload)
const adminSubject = `WONDIF - Candidature projet lycee - ${payload.schoolName}`
const adminText = [
"Une nouvelle candidature a été envoyée depuis le formulaire Projet lycée.",
"",
summary,
].join("\n")
const userSubject = "ONDIF - Confirmation de votre candidature - Projet lycée"
const userText = [
`Bonjour ${payload.coordinatorName},`,
"",
"Votre candidature à un projet avec l'Orchestre national d'Île-de-France a bien été transmise.",
"L'équipe d'action culturelle reviendra vers vous d'ici fin juin 2026 afin de vous informer de la suite donnée à votre candidature.",
"",
"Recapitulatif :",
summary,
].join("\n")
await Promise.all([
mailer.sendMail({
from,
to: config.scolaireRequestRecipientEmail,
replyTo: payload.email,
subject: adminSubject,
text: adminText,
}),
mailer.sendMail({
from,
to: payload.email,
subject: userSubject,
text: userText,
}),
])
return true
}
function formatAcademieDiploma(diploma, index) {
if (!diploma?.type && !diploma?.discipline && !diploma?.year && !diploma?.establishment) {
return `Diplome ${index + 1} : Non renseigne`
}
return [
`Diplome ${index + 1} :`,
`Type : ${diploma.type || "Non renseigne"}`,
diploma.type === "Autre" ? `Autre type : ${diploma.otherType || "Non renseigne"}` : null,
`Discipline : ${diploma.discipline || "Non renseignee"}`,
`Annee : ${diploma.year || "Non renseignee"}`,
`Etablissement : ${diploma.establishment || "Non renseigne"}`,
].filter(Boolean).join("\n")
}
function getProjetAcademieSummary(payload) {
return [
"Identite :",
`Nom : ${payload.lastName}`,
`Prenom : ${payload.firstName}`,
`Genre : ${payload.gender}`,
`Date de naissance : ${payload.birthDate}`,
`Lieu de naissance : ${payload.birthPlace}`,
`Nationalite : ${payload.nationality}`,
"",
"Coordonnees :",
`Email : ${payload.email}`,
`Telephone : ${payload.phone}`,
`Adresse : ${payload.address}`,
`Ville : ${payload.city}`,
`Code postal : ${payload.postalCode}`,
`Pays de residence : ${payload.country}`,
"",
"Parcours musical :",
`Instrument : ${payload.instrument}`,
`A deja participe a l'academie : ${payload.previousAcademyParticipation}`,
payload.previousAcademyParticipation === "oui"
? `Nombre de participations : ${payload.previousAcademyParticipationCount}`
: null,
"",
"Formation :",
`Niveau - cycle actuel 2026-2027 : ${payload.trainingLevel}`,
payload.trainingLevel === "Autre" ? `Autre formation : ${payload.otherTrainingLevel}` : null,
`Conservatoire 2026-2027 : ${payload.conservatory}`,
payload.conservatory === "Autre" ? `Autre conservatoire : ${payload.otherConservatory}` : null,
`Professeur d'instrument : ${payload.teacherName}`,
"",
"Diplomes musicaux :",
...payload.diplomas.map(formatAcademieDiploma),
"",
"Contact d'urgence :",
`Nom : ${payload.emergencyLastName}`,
`Prenom : ${payload.emergencyFirstName}`,
`Lien : ${payload.emergencyRelation}`,
`Telephone : ${payload.emergencyPhone}`,
`Mail : ${payload.emergencyEmail}`,
"",
`Reglement accepte : ${payload.acceptRules ? "Oui" : "Non"}`,
].filter((line) => line !== null).join("\n")
}
export async function sendProjetAcademieEmails(payload) {
const config = useRuntimeConfig()
const smtpConfig = getSmtpConfig(config, "aec")
const mailer = getTransporter("aec")
if (!mailer) {
console.warn("Email projet académie non envoyé: configuration SMTP incomplète.")
return false
}
if (!config.academieRequestRecipientEmail) {
console.warn("Email projet académie non envoyé: destinataire académie manquant.")
return false
}
const from = getFrom(smtpConfig)
const summary = getProjetAcademieSummary(payload)
const fullName = `${payload.lastName} ${payload.firstName}`
const dropboxUrl = "https://www.dropbox.com/scl/fo/nu76b9k860kxkczlgpvwc/AO3pMAvoG9EVcsiRWcRk4cE?rlkey=rjnsqvgl9v1topm8mno3uns4a&e=2&st=dsrnbdca&dl=0"
const siteUrl = process.env.NUXT_PUBLIC_SITE_URL || "https://www.orchestre-ile.com"
const academieUrl = new URL("/mediation/academie", siteUrl).toString()
const adminSubject = `WONDIF - Candidature Academie d'orchestre - ${fullName}`
const adminText = [
"Une nouvelle candidature a été envoyée depuis le formulaire Académie d'orchestre.",
"",
summary,
].join("\n")
const userSubject = "ONDIF - Confirmation de votre candidature - Académie d'orchestre"
const userText = [
`Bonjour ${payload.firstName} ${payload.lastName},`,
"",
`Nous vous confirmons que votre inscription à lAcadémie dOrchestre 2027 a bien été prise en compte pour linstrument suivant : ${payload.instrument}.`,
"",
"Afin de préparer les auditions, vous trouverez ci-dessous un lien vers les cahiers de traits dorchestre à travailler :",
`Lien traits dorchestre (Dropbox) - ${dropboxUrl}`,
"",
"Vous pourrez accéder au cahier correspondant à linstrument présenté aux auditions et commencer votre préparation dès à présent.",
`Ce lien est également accessible depuis la page Académie 2027 du site de lOrchestre national dÎle-de-France : ${academieUrl}`,
"",
"Une fois les inscriptions clôturées, vous recevrez une convocation pour les auditions, qui se dérouleront entre le 2 et le 16 novembre 2026. Cette convocation vous sera envoyée début octobre.",
"",
"Nous vous remercions de bien vouloir apporter le jour des auditions :",
"- la lettre dengagement signée",
"- le formulaire de droit à limage, complété et signé",
"",
"Vous trouverez ces deux documents en pièces jointes de ce message.",
"",
"Pour toute question ou besoin de renseignement complémentaire, vous pouvez contacter Zoe Crampon :",
"par mail : zoe.crampon@orchestre-ile.com",
"par téléphone : 06 82 73 65 01",
"",
"Nous vous remercions de votre inscription et vous souhaitons une excellente préparation.",
"",
"Bien cordialement,",
"Léquipe daction culturelle de lOrchestre national dÎle-de-France",
].join("\n")
const userHtml = [
`<p>Bonjour ${escapeHtml(payload.firstName)} ${escapeHtml(payload.lastName)},</p>`,
`<p>Nous vous confirmons que votre inscription à lAcadémie dOrchestre 2027 a bien été prise en compte pour linstrument suivant : ${escapeHtml(payload.instrument)}.</p>`,
"<p>Afin de préparer les auditions, vous trouverez ci-dessous un lien vers les cahiers de traits dorchestre à travailler :</p>",
`<p><a href="${dropboxUrl}">Lien traits dorchestre (Dropbox)</a></p>`,
"<p>Vous pourrez accéder au cahier correspondant à linstrument présenté aux auditions et commencer votre préparation dès à présent.</p>",
`<p>Ce lien est également accessible depuis la page <a href="${academieUrl}">Académie 2027</a> du site de lOrchestre national dÎle-de-France.</p>`,
"<p>Une fois les inscriptions clôturées, vous recevrez une convocation pour les auditions, qui se dérouleront entre le 2 et le 16 novembre 2026. Cette convocation vous sera envoyée début octobre.</p>",
"<p>Nous vous remercions de bien vouloir apporter le jour des auditions :</p>",
"<ul><li>la lettre dengagement signée</li><li>le formulaire de droit à limage, complété et signé</li></ul>",
"<p>Vous trouverez ces deux documents en pièces jointes de ce message.</p>",
"<p>Pour toute question ou besoin de renseignement complémentaire, vous pouvez contacter Zoe Crampon :<br>par mail : zoe.crampon@orchestre-ile.com<br>par téléphone : 06 82 73 65 01</p>",
"<p>Nous vous remercions de votre inscription et vous souhaitons une excellente préparation.</p>",
"<p>Bien cordialement,<br>Léquipe daction culturelle de lOrchestre national dÎle-de-France</p>",
].join("\n")
await Promise.all([
mailer.sendMail({
from,
to: config.academieRequestRecipientEmail,
subject: adminSubject,
text: adminText,
}),
mailer.sendMail({
from,
to: payload.email,
subject: userSubject,
text: userText,
html: userHtml,
attachments: [
{
filename: "lettre_engagement_academie_2027.pdf",
path: "public/contenus/lettre_engagement_academie_2027.pdf",
},
{
filename: "droit_image_majeur.pdf",
path: "public/contenus/droit_image_majeur.pdf",
},
{
filename: "droit_image_mineur.pdf",
path: "public/contenus/droit_image_mineur.pdf",
},
],
}),
])
return true
}