import { BaseCommand, args, flags } from '@adonisjs/core/ace' import type { CommandOptions } from '@adonisjs/core/types/ace' import mail from '@adonisjs/mail/services/main' import { render } from '@react-email/components' import { DateTime } from 'luxon' import env from '#start/env' import { CheckinEmail } from '#mails/checkin_email' import { DEFAULT_BRAND } from '#services/brand' import { RelanceEmail } from '#mails/relance_email' /** * Envoie un email de test via le mailer courant (typiquement Resend) * pour valider la conf SPF/DKIM/clé API ET le rendu visuel des templates * HTML React Email — sans passer par toute la chaîne facture → BullMQ. * * Usage : * node ace send:test-email arthur@example.com # checkin * node ace send:test-email arthur@example.com --template=checkin * node ace send:test-email arthur@example.com --template=relance * node ace send:test-email arthur@example.com --template=plain # ancien * node ace send:test-email arthur@example.com --reply-to=patron@tpe.fr * * Default `checkin` car c'est le template avec les boutons CTA — le plus * intéressant à valider visuellement au premier coup d'œil. */ export default class SendTestEmail extends BaseCommand { static commandName = 'send:test-email' static description = 'Envoie un email de test (HTML React Email) pour valider rendu + driver' static options: CommandOptions = { startApp: true, } @args.string({ description: 'Adresse destinataire' }) declare to: string @flags.string({ description: 'Template à envoyer : checkin (default) | relance | plain', default: 'checkin', }) declare template: string @flags.string({ description: 'Adresse de reply-to (optionnelle)' }) declare replyTo?: string async run() { const driver = env.get('MAIL_DRIVER', 'smtp') const fromAddress = env.get('MAIL_FROM_ADDRESS', 'relances@rubis.pro') const fromName = env.get('MAIL_FROM_NAME', "Rubis sur l'ongle") const landingUrl = env.get('LANDING_URL', 'https://rubis.pro') this.logger.info(`Driver: ${driver}`) this.logger.info(`From: ${fromName} <${fromAddress}>`) this.logger.info(`To: ${this.to}`) this.logger.info(`Template: ${this.template}`) if (this.replyTo) this.logger.info(`ReplyTo: ${this.replyTo}`) // Données factices pour le rendu — évite d'avoir à créer une vraie // facture en DB juste pour tester l'email. const fakeInvoice = { numero: 'F2026-TEST', amountFormatted: '1 234,56 €', dueDateFormatted: DateTime.utc().toFormat('dd/LL/yyyy'), } let subject: string let text: string let html: string | undefined if (this.template === 'plain') { subject = "[Rubis] Test d'envoi (plain text)" text = `Bonjour,\n\n` + `Ceci est un email de test envoyé depuis Rubis sur l'ongle.\n` + `Driver : ${driver}\n` + `Date : ${new Date().toISOString()}\n\n` + `— L'équipe Rubis` html = undefined } else if (this.template === 'relance') { subject = `Facture ${fakeInvoice.numero} échue (test)` text = `Bonjour,\n\nPetit rappel pour la facture ${fakeInvoice.numero}.\n\nCordialement,\nArthur Barré` html = await render( RelanceEmail({ tokens: { ...DEFAULT_BRAND, senderName: 'Arthur Barré (test)' }, invoice: { ...fakeInvoice, daysLate: 3 }, bodyText: text, landingUrl, }) ) } else { // checkin par défaut subject = `[Rubis] Test — Facture ${fakeInvoice.numero}, payée ?` text = `Test d'envoi du template check-in.\n\n` + `Lien "payée" : https://example.com/paid\n` + `Lien "pending" : https://example.com/pending\n\n` + `— L'équipe Rubis` html = await render( CheckinEmail({ tokens: DEFAULT_BRAND, invoice: fakeInvoice, client: { name: 'Boulangerie Test' }, user: { fullName: 'Arthur Barré' }, paidUrl: 'https://example.com/paid', pendingUrl: 'https://example.com/pending', landingUrl, }) ) } const mailer = mail.use(driver) const response = await mailer.send((m) => { m.from(fromAddress, fromName).to(this.to).subject(subject).text(text) if (html) m.html(html) if (this.replyTo) m.replyTo(this.replyTo) }) this.logger.success('Email envoyé') if (response?.messageId) { this.logger.info(`messageId: ${response.messageId}`) } } }