314 lines
No EOL
10 KiB
JavaScript
314 lines
No EOL
10 KiB
JavaScript
const express = require('express');
|
|
const fs = require('fs').promises;
|
|
const path = require('path');
|
|
const router = express.Router();
|
|
const SystemSettings = require('../models/SystemSettings');
|
|
const config = require('../config');
|
|
|
|
module.exports = (pool, query, authMiddleware) => {
|
|
// Apply authentication middleware to all routes
|
|
router.use(authMiddleware);
|
|
|
|
/**
|
|
* Get all settings
|
|
*/
|
|
router.get('/', async (req, res, next) => {
|
|
try {
|
|
// Check if user is admin
|
|
if (!req.user.is_admin) {
|
|
return res.status(403).json({
|
|
error: true,
|
|
message: 'Admin access required'
|
|
});
|
|
}
|
|
|
|
const settings = await SystemSettings.getAllSettings(pool, query);
|
|
|
|
// Group settings by category
|
|
const groupedSettings = settings.reduce((acc, setting) => {
|
|
if (!acc[setting.category]) {
|
|
acc[setting.category] = [];
|
|
}
|
|
acc[setting.category].push(setting);
|
|
return acc;
|
|
}, {});
|
|
|
|
res.json(groupedSettings);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get settings by category
|
|
*/
|
|
router.get('/category/:category', async (req, res, next) => {
|
|
try {
|
|
const { category } = req.params;
|
|
|
|
// Check if user is admin
|
|
if (!req.user.is_admin) {
|
|
return res.status(403).json({
|
|
error: true,
|
|
message: 'Admin access required'
|
|
});
|
|
}
|
|
|
|
const settings = await SystemSettings.getSettingsByCategory(pool, query, category);
|
|
res.json(settings);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Get a specific setting
|
|
*/
|
|
router.get('/:key', async (req, res, next) => {
|
|
try {
|
|
const { key } = req.params;
|
|
|
|
// Check if user is admin
|
|
if (!req.user.is_admin) {
|
|
return res.status(403).json({
|
|
error: true,
|
|
message: 'Admin access required'
|
|
});
|
|
}
|
|
|
|
const setting = await SystemSettings.getSetting(pool, query, key);
|
|
|
|
if (!setting) {
|
|
return res.status(404).json({
|
|
error: true,
|
|
message: `Setting with key "${key}" not found`
|
|
});
|
|
}
|
|
|
|
res.json(setting);
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Update a single setting
|
|
*/
|
|
router.put('/:key', async (req, res, next) => {
|
|
try {
|
|
const { key } = req.params;
|
|
const { value, category } = req.body;
|
|
|
|
// Check if user is admin
|
|
if (!req.user.is_admin) {
|
|
return res.status(403).json({
|
|
error: true,
|
|
message: 'Admin access required'
|
|
});
|
|
}
|
|
|
|
if (value === undefined || !category) {
|
|
return res.status(400).json({
|
|
error: true,
|
|
message: 'Value and category are required'
|
|
});
|
|
}
|
|
|
|
const updatedSetting = await SystemSettings.updateSetting(pool, query, key, value, category);
|
|
|
|
// Update config in memory
|
|
updateConfigInMemory(updatedSetting);
|
|
|
|
// Update environment file
|
|
await updateEnvironmentFile();
|
|
|
|
res.json({
|
|
message: 'Setting updated successfully',
|
|
setting: updatedSetting
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Update multiple settings at once
|
|
*/
|
|
router.post('/batch', async (req, res, next) => {
|
|
try {
|
|
const { settings } = req.body;
|
|
|
|
// Check if user is admin
|
|
if (!req.user.is_admin) {
|
|
return res.status(403).json({
|
|
error: true,
|
|
message: 'Admin access required'
|
|
});
|
|
}
|
|
|
|
if (!Array.isArray(settings) || settings.length === 0) {
|
|
return res.status(400).json({
|
|
error: true,
|
|
message: 'Settings array is required'
|
|
});
|
|
}
|
|
|
|
// Validate all settings have required fields
|
|
for (const setting of settings) {
|
|
if (!setting.key || setting.value === undefined || !setting.category) {
|
|
return res.status(400).json({
|
|
error: true,
|
|
message: 'Each setting must have key, value, and category fields'
|
|
});
|
|
}
|
|
}
|
|
|
|
const updatedSettings = await SystemSettings.updateSettings(pool, query, settings);
|
|
|
|
// Update config in memory for each setting
|
|
updatedSettings.forEach(setting => {
|
|
updateConfigInMemory(setting);
|
|
});
|
|
|
|
// Update environment file
|
|
await updateEnvironmentFile();
|
|
|
|
res.json({
|
|
message: 'Settings updated successfully',
|
|
settings: updatedSettings
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Delete a setting
|
|
*/
|
|
router.delete('/:key', async (req, res, next) => {
|
|
try {
|
|
const { key } = req.params;
|
|
|
|
// Check if user is admin
|
|
if (!req.user.is_admin) {
|
|
return res.status(403).json({
|
|
error: true,
|
|
message: 'Admin access required'
|
|
});
|
|
}
|
|
|
|
await SystemSettings.deleteSetting(pool, query, key);
|
|
|
|
// Update environment file
|
|
await updateEnvironmentFile();
|
|
|
|
res.json({
|
|
message: `Setting "${key}" deleted successfully`
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Helper function to update config in memory
|
|
*/
|
|
function updateConfigInMemory(setting) {
|
|
const { key, value, category } = setting;
|
|
|
|
// Map database settings to config structure
|
|
if (category === 'email') {
|
|
if (key === 'smtp_host') config.email.host = value;
|
|
if (key === 'smtp_port') config.email.port = parseInt(value, 10);
|
|
if (key === 'smtp_user') config.email.user = value;
|
|
if (key === 'smtp_password') config.email.pass = value;
|
|
if (key === 'smtp_from_email') config.email.reply = value;
|
|
} else if (category === 'site') {
|
|
if (key === 'site_name') config.site.name = value;
|
|
if (key === 'site_domain') config.site.domain = value;
|
|
if (key === 'site_api_domain') config.site.apiDomain = value;
|
|
if (key === 'site_protocol') config.site.protocol = value;
|
|
if (key === 'site_environment') config.environment = value;
|
|
}
|
|
|
|
// You can add more mappings for other categories here
|
|
}
|
|
|
|
/**
|
|
* Helper function to update environment file
|
|
*/
|
|
async function updateEnvironmentFile() {
|
|
try {
|
|
// Get all settings from database
|
|
const allSettings = await SystemSettings.getAllSettings(pool, query);
|
|
config.updateFromDatabase(allSettings)
|
|
// Build environment variables string
|
|
let envContent = '';
|
|
|
|
// Add standard environment variables
|
|
envContent += `PORT=${process.env.PORT || config.port || 4000}\n`;
|
|
envContent += `NODE_ENV=${process.env.NODE_ENV || 'development'}\n\n`;
|
|
|
|
// Add database configuration - use existing config values as fallbacks
|
|
envContent += `# Database connection\n`;
|
|
envContent += `DB_HOST=${config.db.host}\n`;
|
|
envContent += `DB_USER=${config.db.user}\n`;
|
|
envContent += `DB_PASSWORD=${config.db.password}\n`;
|
|
envContent += `DB_NAME=${config.db.database}\n`;
|
|
envContent += `DB_PORT=${config.db.port}\n`;
|
|
envContent += `POSTGRES_USER=${config.db.user}\n`;
|
|
envContent += `POSTGRES_PASSWORD=${config.db.password}\n`;
|
|
envContent += `POSTGRES_DB=${config.db.database}\n`;
|
|
|
|
// Get site environment from settings or use existing config
|
|
const siteEnvSetting = allSettings.find(s => s.key === 'site_environment');
|
|
const currentEnvironment = siteEnvSetting?.value || config.environment || process.env.ENVIRONMENT || 'beta';
|
|
envContent += `ENVIRONMENT=${currentEnvironment}\n\n`;
|
|
|
|
// Add email configuration with fallbacks to existing config
|
|
envContent += `# Email configuration\n`;
|
|
const emailSettings = allSettings.filter(s => s.category === 'email');
|
|
|
|
const smtpHost = emailSettings.find(s => s.key === 'smtp_host');
|
|
const smtpPort = emailSettings.find(s => s.key === 'smtp_port');
|
|
const smtpUser = emailSettings.find(s => s.key === 'smtp_user');
|
|
const smtpPass = emailSettings.find(s => s.key === 'smtp_password');
|
|
const smtpReply = emailSettings.find(s => s.key === 'smtp_from_email');
|
|
|
|
// Use existing config values as fallbacks in this order: database setting → config value → env value → default
|
|
envContent += `EMAIL_HOST=${smtpHost?.value || config.email.host || process.env.EMAIL_HOST || 'smtp.postmarkapp.com'}\n`;
|
|
envContent += `EMAIL_PORT=${smtpPort?.value || config.email.port || process.env.EMAIL_PORT || '587'}\n`;
|
|
envContent += `EMAIL_USER=${smtpUser?.value || config.email.user || process.env.EMAIL_USER || ''}\n`;
|
|
envContent += `EMAIL_PASS=${smtpPass?.value || config.email.pass || process.env.EMAIL_PASS || ''}\n`;
|
|
envContent += `EMAIL_REPLY=${smtpReply?.value || config.email.reply || process.env.EMAIL_REPLY || 'noreply@2many.ca'}\n\n`;
|
|
|
|
// Add payment configuration with fallbacks to existing values
|
|
const paymentSettings = allSettings.filter(s => s.category === 'payment');
|
|
if (paymentSettings.length > 0 || process.env.STRIPE_PUBLIC_KEY || process.env.STRIPE_SECRET_KEY) {
|
|
envContent += `# Payment configuration\n`;
|
|
const stripePublic = paymentSettings.find(s => s.key === 'stripe_public_key');
|
|
const stripeSecret = paymentSettings.find(s => s.key === 'stripe_secret_key');
|
|
|
|
// Include payment settings if they exist in either DB or environment
|
|
if (stripePublic?.value || process.env.STRIPE_PUBLIC_KEY) {
|
|
envContent += `STRIPE_PUBLIC_KEY=${stripePublic?.value || process.env.STRIPE_PUBLIC_KEY}\n`;
|
|
}
|
|
if (stripeSecret?.value || process.env.STRIPE_SECRET_KEY) {
|
|
envContent += `STRIPE_SECRET_KEY=${stripeSecret?.value || process.env.STRIPE_SECRET_KEY}\n`;
|
|
}
|
|
}
|
|
|
|
// Write to .env file
|
|
const envPath = path.join(__dirname, '../../.env');
|
|
await fs.writeFile(envPath, envContent);
|
|
|
|
console.log('Environment file updated successfully');
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error updating environment file:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
return router;
|
|
}; |