first commit
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
import express from 'express';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import nodemailer from 'nodemailer';
|
||||
import { createServer as createViteServer } from 'vite';
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
async function startServer() {
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
// API route to send email via SMTP
|
||||
app.post('/api/send-email', async (req, res) => {
|
||||
const { name, email, phone, company, subject, message, isEstimate, estimateSummary } = req.body;
|
||||
|
||||
if (!name || !email) {
|
||||
return res.status(400).json({ error: 'Missing name or email' });
|
||||
}
|
||||
|
||||
// Load SMTP configuration (favoring env variables, falling back to JSON file)
|
||||
const smtpConfig = {
|
||||
host: process.env.SMTP_HOST || 'smtp.khongisa.co.za',
|
||||
port: Number(process.env.SMTP_PORT || 587),
|
||||
secure: process.env.SMTP_SECURE === 'true',
|
||||
user: process.env.SMTP_USER || '',
|
||||
pass: process.env.SMTP_PASS || '',
|
||||
fromEmail: process.env.SMTP_FROM || '',
|
||||
toEmail: process.env.SMTP_TO || 'info@litecharms.co.za',
|
||||
};
|
||||
|
||||
// If environment variables aren't completely defined, try loading from smtp-config.json
|
||||
const configPath = path.join(process.cwd(), 'smtp-config.json');
|
||||
if ((!smtpConfig.user || !smtpConfig.pass || smtpConfig.pass.includes('YOUR_SECURE')) && fs.existsSync(configPath)) {
|
||||
try {
|
||||
const fileData = fs.readFileSync(configPath, 'utf8');
|
||||
const parsed = JSON.parse(fileData);
|
||||
smtpConfig.host = parsed.host || smtpConfig.host;
|
||||
smtpConfig.port = Number(parsed.port || smtpConfig.port);
|
||||
smtpConfig.secure = parsed.secure ?? smtpConfig.secure;
|
||||
smtpConfig.user = parsed.auth?.user || smtpConfig.user;
|
||||
smtpConfig.pass = parsed.auth?.pass || smtpConfig.pass;
|
||||
smtpConfig.fromEmail = parsed.fromEmail || smtpConfig.fromEmail;
|
||||
smtpConfig.toEmail = parsed.toEmail || smtpConfig.toEmail;
|
||||
} catch (err) {
|
||||
console.error('Error reading smtp-config.json:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate Email Content
|
||||
let emailSubject = subject || 'LiteCharms Inquiry';
|
||||
if (isEstimate) {
|
||||
emailSubject = `LiteCharms Project Estimate Proposal: ${subject}`;
|
||||
}
|
||||
|
||||
let emailHtml = `
|
||||
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden; color: #1e293b;">
|
||||
<div style="background-color: #020617; padding: 24px; text-align: center; border-bottom: 2px solid #00bcac;">
|
||||
<h2 style="color: #ffffff; margin: 0; font-size: 22px; letter-spacing: -0.02em;">Lite<span style="color: #00bcac;">Charms</span></h2>
|
||||
<p style="color: #94a3b8; margin: 4px 0 0 0; font-size: 12px; font-family: monospace;">MIDRAND, SOUTH AFRICA</p>
|
||||
</div>
|
||||
<div style="padding: 24px; background-color: #ffffff;">
|
||||
<h3 style="color: #00bcac; margin-top: 0; margin-bottom: 16px; font-size: 18px;">${isEstimate ? 'New Project Estimate Inquiry' : 'New General Service Inquiry'}</h3>
|
||||
|
||||
<table style="width: 100%; border-collapse: collapse; margin-bottom: 24px;">
|
||||
<tr style="border-bottom: 1px solid #f1f5f9;">
|
||||
<td style="padding: 8px 0; font-weight: bold; width: 120px; font-size: 13px; color: #64748b;">Client Name:</td>
|
||||
<td style="padding: 8px 0; font-size: 13px; color: #1e293b;">${name}</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid #f1f5f9;">
|
||||
<td style="padding: 8px 0; font-weight: bold; font-size: 13px; color: #64748b;">Email Address:</td>
|
||||
<td style="padding: 8px 0; font-size: 13px; color: #1e293b;"><a href="mailto:${email}">${email}</a></td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid #f1f5f9;">
|
||||
<td style="padding: 8px 0; font-weight: bold; font-size: 13px; color: #64748b;">Phone Number:</td>
|
||||
<td style="padding: 8px 0; font-size: 13px; color: #1e293b;">${phone || 'Not Provided'}</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid #f1f5f9;">
|
||||
<td style="padding: 8px 0; font-weight: bold; font-size: 13px; color: #64748b;">Company:</td>
|
||||
<td style="padding: 8px 0; font-size: 13px; color: #1e293b;">${company || 'Not Provided'}</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid #f1f5f9;">
|
||||
<td style="padding: 8px 0; font-weight: bold; font-size: 13px; color: #64748b;">Subject:</td>
|
||||
<td style="padding: 8px 0; font-size: 13px; color: #1e293b;">${subject || 'General Service Contact'}</td>
|
||||
</tr>
|
||||
</table>
|
||||
`;
|
||||
|
||||
if (isEstimate && estimateSummary) {
|
||||
emailHtml += `
|
||||
<div style="background-color: #f8fafc; border: 1px dashed #cbd5e1; padding: 16px; border-radius: 6px; margin-bottom: 24px;">
|
||||
<h4 style="margin-top: 0; color: #020617; font-size: 14px; border-bottom: 1px solid #e2e8f0; padding-bottom: 6px;">Selected Scope & Custom Pricing Range</h4>
|
||||
<div style="margin-top: 8px; font-size: 13px;">
|
||||
<strong>Estimated Range:</strong> <span style="color: #00bcac; font-weight: bold;">R ${estimateSummary.totalMin?.toLocaleString('en-ZA')}</span> to <span style="color: #e11d48; font-weight: bold;">R ${estimateSummary.totalMax?.toLocaleString('en-ZA')}</span>
|
||||
<br/><strong>Roadmap:</strong> ~${estimateSummary.timelineWeeks} Weeks
|
||||
</div>
|
||||
<div style="margin-top: 12px; font-size: 11px; color: #64748b;">
|
||||
<strong>Breakdown items:</strong>
|
||||
<ul style="margin: 4px 0 0 0; padding-left: 16px;">
|
||||
${(estimateSummary.breakdown || []).map((b: any) => `<li>${b.item}: ${b.price}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
emailHtml += `
|
||||
<div style="margin-bottom: 24px;">
|
||||
<h4 style="margin-top: 0; margin-bottom: 8px; font-size: 14px; color: #020617;">Client Message / Brief:</h4>
|
||||
<p style="background-color: #f8fafc; padding: 12px; border-radius: 6px; font-size: 13px; line-height: 1.5; color: #334155; white-space: pre-wrap; margin: 0;">${message || 'No additional notes provided.'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background-color: #f1f5f9; padding: 16px; text-align: center; font-size: 11px; color: #64748b; border-top: 1px solid #e2e8f0;">
|
||||
This is an automated dispatch from khongisa.co.za Private Cloud platform for LiteCharms (PTY) Ltd.
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Check if SMTP is ready or left as placeholder
|
||||
const hasValidAuth = smtpConfig.user && smtpConfig.pass && !smtpConfig.pass.includes('YOUR_SECURE');
|
||||
|
||||
if (!hasValidAuth) {
|
||||
console.log('\n--- SMTP EMAIL LOG (NO PASSWORD CONFIGURED) ---');
|
||||
console.log(`To: ${smtpConfig.toEmail}`);
|
||||
console.log(`From: ${smtpConfig.fromEmail || 'no-reply@khongisa.co.za'}`);
|
||||
console.log(`Subject: ${emailSubject}`);
|
||||
console.log(`Plain text summary:\nClient ${name} (${email}) sent inquiry. Subject: ${emailSubject}`);
|
||||
console.log('-----------------------------------------------\n');
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: 'SMTP credentials not configured yet. Email dispatch was logged in system console successfully.',
|
||||
logged: true
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: smtpConfig.host,
|
||||
port: smtpConfig.port,
|
||||
secure: smtpConfig.secure,
|
||||
auth: {
|
||||
user: smtpConfig.user,
|
||||
pass: smtpConfig.pass,
|
||||
},
|
||||
});
|
||||
|
||||
await transporter.sendMail({
|
||||
from: smtpConfig.fromEmail || smtpConfig.user,
|
||||
to: smtpConfig.toEmail,
|
||||
subject: emailSubject,
|
||||
html: emailHtml,
|
||||
});
|
||||
|
||||
return res.json({ success: true, message: 'Email sent successfully via SMTP' });
|
||||
} catch (error: any) {
|
||||
console.error('SMTP Email Send Error:', error);
|
||||
return res.status(500).json({
|
||||
error: 'SMTP Connection failed',
|
||||
details: error.message,
|
||||
message: 'Failed to send email via SMTP post-deployment. Please verify your host, user, and password inside smtp-config.json.'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Vite middleware setup
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const vite = await createViteServer({
|
||||
server: { middlewareMode: true },
|
||||
appType: 'spa',
|
||||
});
|
||||
app.use(vite.middlewares);
|
||||
} else {
|
||||
const distPath = path.join(process.cwd(), 'dist');
|
||||
app.use(express.static(distPath));
|
||||
app.get('*', (req, res) => {
|
||||
res.sendFile(path.join(distPath, 'index.html'));
|
||||
});
|
||||
}
|
||||
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`LiteCharms full-stack application running on http://localhost:${PORT}`);
|
||||
});
|
||||
}
|
||||
|
||||
startServer();
|
||||
Reference in New Issue
Block a user