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:
gowtham-darkseid
2025-09-09 21:17:46 +05:30
commit 44ea4d2e64
21 changed files with 4697 additions and 0 deletions
+180
View File
@@ -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
};
}
});
+25
View File
@@ -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
}