feat(mail): clarify mailgun configuration feedback

This commit is contained in:
William Valentin
2025-09-23 10:30:12 -07:00
parent 71c37f4b7b
commit e7dbe30763
3 changed files with 246 additions and 42 deletions

View File

@@ -5,6 +5,8 @@
import { getMailgunConfig, type MailgunConfig } from './mailgun.config';
import { getAppConfig } from '../config/unified.config';
import { logger } from './logging';
import { normalizeError } from '../utils/error';
interface EmailTemplate {
subject: string;
@@ -14,24 +16,34 @@ interface EmailTemplate {
export class MailgunService {
private config: MailgunConfig;
private readonly context = 'MAILGUN';
constructor() {
this.config = getMailgunConfig();
// Log configuration status on startup
const status = this.getConfigurationStatus();
if (status.mode === 'development') {
console.warn(
'📧 Mailgun Service: Running in development mode (emails will be logged only)'
if (!status.configured) {
logger.warn(
'Mailgun running in development mode; emails will not be delivered',
this.context,
{
missingFields: status.missingFields,
domain: status.domain,
}
);
console.warn(
'💡 To enable real emails, configure Mailgun credentials in .env.local'
logger.info(
'To enable email delivery, configure Mailgun environment variables',
this.context,
{
requiredVariables: status.missingFields,
}
);
} else {
console.warn(
'📧 Mailgun Service: Configured for production with domain:',
status.domain
);
logger.info('Mailgun configured for delivery', this.context, {
domain: status.domain,
fromEmail: status.fromEmail,
});
}
}
@@ -95,6 +107,27 @@ export class MailgunService {
async sendEmail(to: string, template: EmailTemplate): Promise<boolean> {
try {
const status = this.getConfigurationStatus();
if (!status.configured) {
logger.warn(
'Skipping email send; Mailgun is not configured',
this.context,
{
to,
subject: template.subject,
missingFields: status.missingFields,
preview: status.mode === 'development',
}
);
logger.debug('Mailgun email preview', this.context, {
to,
subject: template.subject,
html: template.html,
text: template.text,
});
return false;
}
// Production Mailgun API call
const formData = new FormData();
formData.append(
@@ -125,7 +158,7 @@ export class MailgunService {
}
const result = await response.json();
console.warn('📧 Email sent successfully via Mailgun:', {
logger.info('Email sent via Mailgun', this.context, {
to,
subject: template.subject,
messageId: result.id,
@@ -133,7 +166,14 @@ export class MailgunService {
return true;
} catch (error: unknown) {
console.error('Email sending failed:', error);
logger.error(
'Mailgun email send failed',
this.context,
{
domain: this.config.domain,
},
normalizeError(error)
);
return false;
}
}
@@ -156,13 +196,10 @@ export class MailgunService {
mode: 'development' | 'production';
domain: string;
fromEmail: string;
missingFields: string[];
} {
const configured =
!!this.config.apiKey &&
!!this.config.domain &&
!!this.config.baseUrl &&
!!this.config.fromEmail &&
!!this.config.fromName;
const missingFields = this.getMissingFields();
const configured = missingFields.length === 0;
const mode: 'development' | 'production' = configured
? 'production'
: 'development';
@@ -171,8 +208,35 @@ export class MailgunService {
mode,
domain: this.config.domain,
fromEmail: this.config.fromEmail,
missingFields,
};
}
private getMissingFields(): string[] {
const missing: string[] = [];
if (!this.config.apiKey) {
missing.push('VITE_MAILGUN_API_KEY');
}
if (!this.config.domain) {
missing.push('VITE_MAILGUN_DOMAIN');
}
if (!this.config.fromEmail) {
missing.push('VITE_MAILGUN_FROM_EMAIL');
}
if (!this.config.baseUrl) {
missing.push('VITE_MAILGUN_BASE_URL');
}
if (!this.config.fromName) {
missing.push('VITE_MAILGUN_FROM_NAME');
}
return missing;
}
}
export const mailgunService = new MailgunService();