Bascule du domaine principal vers rubis.pro / app.rubis.pro : - K3s ConfigMaps (api.yml, web.yml) : APP_URL, WEB_URL, COOKIE_DOMAIN, OAUTH callbacks pointent vers app.rubis.pro - Dockerfile.web : ARG VITE_API_URL et VITE_PUBLIC_LANDING_URL - Workflows Gitea : commentaires + build args web → rubis.pro - Code API (mail_dispatcher, send_test_email, config/mail) : defaults env LANDING_URL et MAIL_FROM_ADDRESS migrés - Templates env (.env.example) idem - Docs (architecture, backend, frontend, brand-identity) idem - AGENTS.md / CLAUDE.md / deploy-memory : pointeurs domaine MAJ Note : MAIL_FROM_ADDRESS dans le secret K3s reste sur rubis@arthurbarre.fr tant que le domaine rubis.pro n'est pas Verified dans Resend. À switcher manuellement après vérif Resend. Compat : un 301 Traefik redirige rubis.arthurbarre.fr → rubis.pro (et app.X aussi) — config Ansible dans le repo proxmox. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
124 lines
4.4 KiB
TypeScript
124 lines
4.4 KiB
TypeScript
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 { 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({
|
||
brandName: '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({
|
||
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}`)
|
||
}
|
||
}
|
||
}
|