mirror of
https://github.com/Gowtham-Darkseid/MailComposer.git
synced 2026-06-06 16:43:53 +02:00
feat: Complete Mail Composer with EmailJS integration
- Rich email composer with formatting tools (bold, italic, underline, links) - Multi-recipient support (To, CC, BCC fields) - File attachments with drag & drop support - Priority levels and email validation - Draft management with auto-save and cloud sync - Real email sending via EmailJS integration - Responsive design for mobile and desktop - Comprehensive error handling and fallback modes - Complete documentation and setup guides - Firebase integration ready for advanced features Features: ✅ Real email sending (EmailJS) ✅ Rich text editor ✅ File attachments ✅ Draft management ✅ Form validation ✅ Responsive UI ✅ Error handling ✅ Documentation
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
const {onRequest, onCall} = require("firebase-functions/v2/https");
|
||||
const {logger} = require("firebase-functions");
|
||||
const admin = require('firebase-admin');
|
||||
const nodemailer = require('nodemailer');
|
||||
const cors = require('cors')({origin: true});
|
||||
|
||||
// Initialize Firebase Admin
|
||||
admin.initializeApp();
|
||||
|
||||
// Configure your email service (Gmail example)
|
||||
// You'll need to enable "Less secure app access" or use App Passwords
|
||||
const transporter = nodemailer.createTransporter({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: process.env.GMAIL_USER, // Set in Firebase Functions config
|
||||
pass: process.env.GMAIL_PASSWORD // Set in Firebase Functions config
|
||||
}
|
||||
});
|
||||
|
||||
// Alternative configuration for other email services
|
||||
/*
|
||||
const transporter = nodemailer.createTransporter({
|
||||
host: 'smtp.your-email-provider.com',
|
||||
port: 587,
|
||||
secure: false,
|
||||
auth: {
|
||||
user: process.env.EMAIL_USER,
|
||||
pass: process.env.EMAIL_PASSWORD
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
// Send Email Cloud Function
|
||||
exports.sendEmail = onCall(async (request) => {
|
||||
const data = request.data;
|
||||
|
||||
try {
|
||||
// Validate required fields
|
||||
if (!data.to || !data.subject || !data.message) {
|
||||
throw new Error('Missing required fields: to, subject, or message');
|
||||
}
|
||||
|
||||
// Validate email addresses
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
const validateEmails = (emails) => {
|
||||
return emails.every(email => emailRegex.test(email.trim()));
|
||||
};
|
||||
|
||||
if (!validateEmails(data.to)) {
|
||||
throw new Error('Invalid email addresses in "to" field');
|
||||
}
|
||||
|
||||
if (data.cc && !validateEmails(data.cc)) {
|
||||
throw new Error('Invalid email addresses in "cc" field');
|
||||
}
|
||||
|
||||
if (data.bcc && !validateEmails(data.bcc)) {
|
||||
throw new Error('Invalid email addresses in "bcc" field');
|
||||
}
|
||||
|
||||
// Prepare email options
|
||||
const mailOptions = {
|
||||
from: process.env.GMAIL_USER || 'your-email@gmail.com',
|
||||
to: data.to.join(', '),
|
||||
subject: data.subject,
|
||||
html: data.message,
|
||||
priority: data.priority || 'normal'
|
||||
};
|
||||
|
||||
// Add CC and BCC if provided
|
||||
if (data.cc && data.cc.length > 0) {
|
||||
mailOptions.cc = data.cc.join(', ');
|
||||
}
|
||||
|
||||
if (data.bcc && data.bcc.length > 0) {
|
||||
mailOptions.bcc = data.bcc.join(', ');
|
||||
}
|
||||
|
||||
// Set priority header
|
||||
if (data.priority === 'high') {
|
||||
mailOptions.priority = 'high';
|
||||
mailOptions.headers = { 'X-Priority': '1' };
|
||||
} else if (data.priority === 'low') {
|
||||
mailOptions.priority = 'low';
|
||||
mailOptions.headers = { 'X-Priority': '5' };
|
||||
}
|
||||
|
||||
// Send the email
|
||||
const info = await transporter.sendMail(mailOptions);
|
||||
|
||||
logger.info('Email sent successfully:', info.messageId);
|
||||
|
||||
// Log to Firestore for tracking
|
||||
await admin.firestore().collection('emailLogs').add({
|
||||
to: data.to,
|
||||
cc: data.cc || [],
|
||||
bcc: data.bcc || [],
|
||||
subject: data.subject,
|
||||
messageId: info.messageId,
|
||||
timestamp: admin.firestore.FieldValue.serverTimestamp(),
|
||||
status: 'sent'
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
messageId: info.messageId,
|
||||
message: 'Email sent successfully'
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Error sending email:', error);
|
||||
|
||||
// Log failed attempt
|
||||
await admin.firestore().collection('emailLogs').add({
|
||||
to: data.to,
|
||||
subject: data.subject,
|
||||
error: error.message,
|
||||
timestamp: admin.firestore.FieldValue.serverTimestamp(),
|
||||
status: 'failed'
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Get Email Logs (for admin purposes)
|
||||
exports.getEmailLogs = onCall(async (request) => {
|
||||
try {
|
||||
const snapshot = await admin.firestore()
|
||||
.collection('emailLogs')
|
||||
.orderBy('timestamp', 'desc')
|
||||
.limit(50)
|
||||
.get();
|
||||
|
||||
const logs = [];
|
||||
snapshot.forEach(doc => {
|
||||
logs.push({
|
||||
id: doc.id,
|
||||
...doc.data()
|
||||
});
|
||||
});
|
||||
|
||||
return { success: true, logs };
|
||||
} catch (error) {
|
||||
logger.error('Error getting email logs:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
// Health check endpoint
|
||||
exports.healthCheck = onRequest(async (req, res) => {
|
||||
cors(req, res, () => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
timestamp: new Date().toISOString(),
|
||||
service: 'Mail Composer Functions'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Test email configuration
|
||||
exports.testEmailConfig = onCall(async (request) => {
|
||||
try {
|
||||
// Verify transporter configuration
|
||||
await transporter.verify();
|
||||
return {
|
||||
success: true,
|
||||
message: 'Email configuration is valid'
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Email configuration test failed:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase emulators:start --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18"
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"firebase-admin": "^12.1.0",
|
||||
"firebase-functions": "^5.0.0",
|
||||
"nodemailer": "^6.9.0",
|
||||
"cors": "^2.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"firebase-functions-test": "^3.1.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
Reference in New Issue
Block a user