Replace all mock data with real backend integration
- Add database tables for email configs, settings, and activity logs - Create backend endpoints for dashboard stats, analytics, archives, email config, and settings - Create frontend service layer (admin, archive, email, settings services) - Update AdminDashboard to use real statistics and activity data - Update AnalyticsPage to fetch real analytics from backend - Update ArchivesPage with pagination and real archive operations - Update EmailConfigPage to manage real SMTP config and templates - Remove all mock data and replace with API calls throughout admin interface 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
const express = require('express');
|
||||
const nodemailer = require('nodemailer');
|
||||
const { body, validationResult } = require('express-validator');
|
||||
const { db } = require('../database/db');
|
||||
const { adminAuth } = require('../middleware/auth');
|
||||
const router = express.Router();
|
||||
|
||||
// Get email configuration
|
||||
router.get('/config', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const config = await db('email_configs').first();
|
||||
|
||||
if (!config) {
|
||||
return res.json({
|
||||
smtp_host: '',
|
||||
smtp_port: 587,
|
||||
smtp_secure: false,
|
||||
smtp_user: '',
|
||||
smtp_pass: '', // Don't send actual password
|
||||
from_email: '',
|
||||
from_name: ''
|
||||
});
|
||||
}
|
||||
|
||||
// Don't send the actual password
|
||||
res.json({
|
||||
...config,
|
||||
smtp_pass: config.smtp_pass ? '********' : ''
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Email config fetch error:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch email configuration' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update email configuration
|
||||
router.post('/config', [
|
||||
adminAuth,
|
||||
body('smtp_host').notEmpty().withMessage('SMTP host is required'),
|
||||
body('smtp_port').isInt({ min: 1, max: 65535 }).withMessage('Invalid port number'),
|
||||
body('from_email').isEmail().withMessage('Invalid from email address')
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
const {
|
||||
smtp_host,
|
||||
smtp_port,
|
||||
smtp_secure,
|
||||
smtp_user,
|
||||
smtp_pass,
|
||||
from_email,
|
||||
from_name
|
||||
} = req.body;
|
||||
|
||||
// Check if config exists
|
||||
const existingConfig = await db('email_configs').first();
|
||||
|
||||
const configData = {
|
||||
smtp_host,
|
||||
smtp_port: parseInt(smtp_port),
|
||||
smtp_secure: smtp_secure || false,
|
||||
smtp_user: smtp_user || '',
|
||||
from_email,
|
||||
from_name: from_name || 'Photo Sharing',
|
||||
updated_at: new Date()
|
||||
};
|
||||
|
||||
// Only update password if provided and not masked
|
||||
if (smtp_pass && smtp_pass !== '********') {
|
||||
configData.smtp_pass = smtp_pass;
|
||||
}
|
||||
|
||||
if (existingConfig) {
|
||||
await db('email_configs')
|
||||
.where('id', existingConfig.id)
|
||||
.update(configData);
|
||||
} else {
|
||||
await db('email_configs').insert(configData);
|
||||
}
|
||||
|
||||
// Log activity
|
||||
await db('activity_logs').insert({
|
||||
activity_type: 'email_config_updated',
|
||||
actor_type: 'admin',
|
||||
actor_id: req.user.id,
|
||||
actor_name: req.user.username,
|
||||
metadata: JSON.stringify({ smtp_host, from_email })
|
||||
});
|
||||
|
||||
res.json({ message: 'Email configuration updated successfully' });
|
||||
} catch (error) {
|
||||
console.error('Email config update error:', error);
|
||||
res.status(500).json({ error: 'Failed to update email configuration' });
|
||||
}
|
||||
});
|
||||
|
||||
// Test email configuration
|
||||
router.post('/test', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const { test_email } = req.body;
|
||||
|
||||
if (!test_email) {
|
||||
return res.status(400).json({ error: 'Test email address is required' });
|
||||
}
|
||||
|
||||
// Get email config
|
||||
const config = await db('email_configs').first();
|
||||
|
||||
if (!config) {
|
||||
return res.status(400).json({ error: 'Email configuration not found. Please configure SMTP settings first.' });
|
||||
}
|
||||
|
||||
// Create transporter
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: config.smtp_host,
|
||||
port: config.smtp_port,
|
||||
secure: config.smtp_secure,
|
||||
auth: config.smtp_user ? {
|
||||
user: config.smtp_user,
|
||||
pass: config.smtp_pass
|
||||
} : undefined
|
||||
});
|
||||
|
||||
// Send test email
|
||||
await transporter.sendMail({
|
||||
from: `${config.from_name} <${config.from_email}>`,
|
||||
to: test_email,
|
||||
subject: 'Test Email - Photo Sharing Platform',
|
||||
html: `
|
||||
<h2>Test Email Successful!</h2>
|
||||
<p>This is a test email from your Photo Sharing platform.</p>
|
||||
<p>If you're seeing this, your email configuration is working correctly.</p>
|
||||
<hr>
|
||||
<p style="color: #666; font-size: 12px;">
|
||||
Sent from: ${config.from_email}<br>
|
||||
SMTP Host: ${config.smtp_host}<br>
|
||||
Time: ${new Date().toISOString()}
|
||||
</p>
|
||||
`,
|
||||
text: 'Test Email Successful! Your email configuration is working correctly.'
|
||||
});
|
||||
|
||||
res.json({ message: 'Test email sent successfully' });
|
||||
} catch (error) {
|
||||
console.error('Test email error:', error);
|
||||
res.status(500).json({
|
||||
error: 'Failed to send test email',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Get email templates
|
||||
router.get('/templates', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const templates = await db('email_templates')
|
||||
.select('*')
|
||||
.orderBy('template_key');
|
||||
|
||||
// Parse variables JSON
|
||||
const formattedTemplates = templates.map(template => ({
|
||||
...template,
|
||||
variables: template.variables ? JSON.parse(template.variables) : []
|
||||
}));
|
||||
|
||||
res.json(formattedTemplates);
|
||||
} catch (error) {
|
||||
console.error('Email templates fetch error:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch email templates' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get single template
|
||||
router.get('/templates/:key', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const template = await db('email_templates')
|
||||
.where('template_key', req.params.key)
|
||||
.first();
|
||||
|
||||
if (!template) {
|
||||
return res.status(404).json({ error: 'Template not found' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
...template,
|
||||
variables: template.variables ? JSON.parse(template.variables) : []
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Email template fetch error:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch email template' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update email template
|
||||
router.put('/templates/:key', [
|
||||
adminAuth,
|
||||
body('subject').notEmpty().withMessage('Subject is required'),
|
||||
body('body_html').notEmpty().withMessage('HTML body is required')
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
const { subject, body_html, body_text } = req.body;
|
||||
|
||||
const updated = await db('email_templates')
|
||||
.where('template_key', req.params.key)
|
||||
.update({
|
||||
subject,
|
||||
body_html,
|
||||
body_text: body_text || '',
|
||||
updated_at: new Date()
|
||||
});
|
||||
|
||||
if (!updated) {
|
||||
return res.status(404).json({ error: 'Template not found' });
|
||||
}
|
||||
|
||||
// Log activity
|
||||
await db('activity_logs').insert({
|
||||
activity_type: 'email_template_updated',
|
||||
actor_type: 'admin',
|
||||
actor_id: req.user.id,
|
||||
actor_name: req.user.username,
|
||||
metadata: JSON.stringify({ template_key: req.params.key })
|
||||
});
|
||||
|
||||
res.json({ message: 'Email template updated successfully' });
|
||||
} catch (error) {
|
||||
console.error('Email template update error:', error);
|
||||
res.status(500).json({ error: 'Failed to update email template' });
|
||||
}
|
||||
});
|
||||
|
||||
// Preview email template
|
||||
router.post('/templates/:key/preview', adminAuth, async (req, res) => {
|
||||
try {
|
||||
const template = await db('email_templates')
|
||||
.where('template_key', req.params.key)
|
||||
.first();
|
||||
|
||||
if (!template) {
|
||||
return res.status(404).json({ error: 'Template not found' });
|
||||
}
|
||||
|
||||
const { preview_data } = req.body;
|
||||
|
||||
// Replace variables in template
|
||||
let htmlContent = template.body_html;
|
||||
let textContent = template.body_text || '';
|
||||
let subject = template.subject;
|
||||
|
||||
if (preview_data) {
|
||||
Object.keys(preview_data).forEach(key => {
|
||||
const regex = new RegExp(`{{${key}}}`, 'g');
|
||||
htmlContent = htmlContent.replace(regex, preview_data[key]);
|
||||
textContent = textContent.replace(regex, preview_data[key]);
|
||||
subject = subject.replace(regex, preview_data[key]);
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
subject,
|
||||
body_html: htmlContent,
|
||||
body_text: textContent
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Email template preview error:', error);
|
||||
res.status(500).json({ error: 'Failed to preview email template' });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user