Update server.js

This commit is contained in:
fcarraUniSa
2025-12-23 12:19:41 +01:00
committed by GitHub
parent 4d9e2c78e6
commit d64a8bd89a

133
server.js
View File

@@ -1,3 +1,4 @@
import express from 'express'; import express from 'express';
import path from 'path'; import path from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
@@ -14,12 +15,27 @@ const __dirname = path.dirname(__filename);
const app = express(); const app = express();
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
// 1. CORS deve essere la prima cosa per gestire le richieste OPTIONS dei browser
app.use(cors()); app.use(cors());
app.use(express.json()); app.options('*', cors());
// 2. Logging immediato di ogni richiesta
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} - Status: ${res.statusCode} (${duration}ms)`);
});
next();
});
// 3. Parser JSON con limite aumentato (fondamentale per template HTML grandi)
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ limit: '10mb', extended: true }));
// 4. File statici
app.use(express.static(path.join(__dirname, 'dist'))); app.use(express.static(path.join(__dirname, 'dist')));
// DB Configuration
// Normalize DB_TYPE: allow 'postgres', 'postgresql', 'Postgres', etc.
const rawDbType = (process.env.DB_TYPE || 'mysql').toLowerCase().trim(); const rawDbType = (process.env.DB_TYPE || 'mysql').toLowerCase().trim();
const DB_TYPE = (rawDbType === 'postgres' || rawDbType === 'postgresql') ? 'postgres' : 'mysql'; const DB_TYPE = (rawDbType === 'postgres' || rawDbType === 'postgresql') ? 'postgres' : 'mysql';
@@ -31,83 +47,48 @@ const dbConfig = {
port: process.env.DB_PORT || (DB_TYPE === 'postgres' ? 5432 : 3306), port: process.env.DB_PORT || (DB_TYPE === 'postgres' ? 5432 : 3306),
}; };
// Debug Log: Print config to console (masking password) console.log('--- App Version: 1.0.2 ---');
console.log('--- Database Configuration ---'); console.log(`DB Type: ${DB_TYPE}`);
console.log(`Type: ${DB_TYPE} (Input: ${process.env.DB_TYPE || 'default'})`); console.log(`DB Host: ${dbConfig.host}`);
console.log(`Host: ${dbConfig.host}`);
console.log(`User: ${dbConfig.user}`);
console.log(`Database: ${dbConfig.database}`);
console.log(`Port: ${dbConfig.port}`);
console.log(`Password: ${dbConfig.password ? '******' : '(Not Set)'}`);
console.log('------------------------------');
let pool; let pool;
// Initialize DB Connection const initDB = async (retries = 5) => {
const initDB = async () => { while (retries > 0) {
try { try {
if (!dbConfig.host || !dbConfig.user || !dbConfig.database) { if (!dbConfig.host || !dbConfig.user || !dbConfig.database) {
throw new Error("Missing required database environment variables (DB_HOST, DB_USER, or DB_NAME)."); throw new Error("Missing required database environment variables (DB_HOST, DB_USER, DB_NAME).");
} }
if (DB_TYPE === 'postgres') { if (DB_TYPE === 'postgres') {
const { Pool } = pg; const { Pool } = pg;
pool = new Pool(dbConfig); pool = new Pool(dbConfig);
await pool.query('SELECT 1'); // Test connection
// Create table for Postgres
// Using JSONB for variables
await pool.query(`
CREATE TABLE IF NOT EXISTS email_templates (
id VARCHAR(255) PRIMARY KEY,
template_key VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
subject VARCHAR(255),
header_html TEXT,
body_html TEXT,
footer_html TEXT,
full_html TEXT,
required_variables JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`);
} else { } else {
// MySQL
pool = mysql.createPool(dbConfig); pool = mysql.createPool(dbConfig);
await pool.query('SELECT 1'); // Test connection
// Create table for MySQL
await pool.query(`
CREATE TABLE IF NOT EXISTS email_templates (
id VARCHAR(255) PRIMARY KEY,
template_key VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
subject VARCHAR(255),
header_html TEXT,
body_html TEXT,
footer_html TEXT,
full_html TEXT,
required_variables JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
`);
} }
console.log(`Connected to ${DB_TYPE} database successfully.`); console.log(`Connected to ${DB_TYPE} database successfully.`);
return;
} catch (err) { } catch (err) {
console.error('Database connection failed:', err); retries -= 1;
process.exit(1); console.error(`Database connection failed (${retries} retries left):`, err.message);
if (retries === 0) {
console.error("FATAL: Could not connect to database.");
// Non usciamo per permettere al server di servire i file statici e mostrare errori via API
} else {
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
} }
}; };
initDB(); initDB();
// API Routes
// GET All Templates
app.get('/api/templates', async (req, res) => { app.get('/api/templates', async (req, res) => {
try { try {
if (!pool) return res.status(503).json({ error: "Database not connected" });
let rows; let rows;
if (DB_TYPE === 'postgres') { if (DB_TYPE === 'postgres') {
const result = await pool.query('SELECT * FROM email_templates ORDER BY updated_at DESC'); const result = await pool.query('SELECT * FROM email_templates ORDER BY updated_at DESC');
@@ -117,7 +98,6 @@ app.get('/api/templates', async (req, res) => {
rows = result; rows = result;
} }
// Map DB fields back to frontend types
const templates = rows.map(row => ({ const templates = rows.map(row => ({
id: row.id, id: row.id,
name: row.name, name: row.name,
@@ -126,21 +106,24 @@ app.get('/api/templates', async (req, res) => {
header: row.header_html, header: row.header_html,
body: row.body_html, body: row.body_html,
footer: row.footer_html, footer: row.footer_html,
variables: typeof row.required_variables === 'string' ? JSON.parse(row.required_variables) : row.required_variables, variables: typeof row.required_variables === 'string' ? JSON.parse(row.required_variables) : (row.required_variables || []),
updatedAt: row.updated_at updatedAt: row.updated_at
})); }));
res.json(templates); res.json(templates);
} catch (err) { } catch (err) {
console.error(err); console.error("Fetch Error:", err);
res.status(500).json({ error: 'Failed to fetch templates' }); res.status(500).json({ error: 'Failed to fetch templates', details: err.message });
} }
}); });
// SAVE (Upsert) Template
app.post('/api/templates', async (req, res) => { app.post('/api/templates', async (req, res) => {
const t = req.body; const t = req.body;
// Ensure default values to prevent undefined errors in Postgres
if (!t.name || !t.id) {
return res.status(400).json({ error: "Name and ID are required" });
}
const header = t.header || ''; const header = t.header || '';
const body = t.body || ''; const body = t.body || '';
const footer = t.footer || ''; const footer = t.footer || '';
@@ -162,8 +145,9 @@ app.post('/api/templates', async (req, res) => {
]; ];
try { try {
if (!pool) throw new Error("Database not connected");
if (DB_TYPE === 'postgres') { if (DB_TYPE === 'postgres') {
// Postgres requires explicit casting for JSONB parameters when passed as strings ($10::jsonb)
const query = ` const query = `
INSERT INTO email_templates (id, template_key, name, description, subject, header_html, body_html, footer_html, full_html, required_variables, updated_at) INSERT INTO email_templates (id, template_key, name, description, subject, header_html, body_html, footer_html, full_html, required_variables, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, NOW()) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::jsonb, NOW())
@@ -182,8 +166,8 @@ app.post('/api/templates', async (req, res) => {
await pool.query(query, params); await pool.query(query, params);
} else { } else {
const query = ` const query = `
INSERT INTO email_templates (id, template_key, name, description, subject, header_html, body_html, footer_html, full_html, required_variables, updated_at) INSERT INTO email_templates (id, template_key, name, description, subject, header_html, body_html, footer_html, full_html, required_variables)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
template_key = VALUES(template_key), template_key = VALUES(template_key),
name = VALUES(name), name = VALUES(name),
@@ -193,22 +177,20 @@ app.post('/api/templates', async (req, res) => {
body_html = VALUES(body_html), body_html = VALUES(body_html),
footer_html = VALUES(footer_html), footer_html = VALUES(footer_html),
full_html = VALUES(full_html), full_html = VALUES(full_html),
required_variables = VALUES(required_variables), required_variables = VALUES(required_variables);
updated_at = NOW();
`; `;
await pool.query(query, params); await pool.query(query, params);
} }
res.json({ success: true }); res.json({ success: true });
} catch (err) { } catch (err) {
console.error("Save Template Error:", err); console.error("DB Save Error:", err.message);
// Return specific error details to help debugging on the client res.status(500).json({ error: 'Database save failed', details: err.message });
res.status(500).json({ error: 'Failed to save template', details: err.message });
} }
}); });
// DELETE Template
app.delete('/api/templates/:id', async (req, res) => { app.delete('/api/templates/:id', async (req, res) => {
try { try {
if (!pool) return res.status(503).json({ error: "Database not connected" });
if (DB_TYPE === 'postgres') { if (DB_TYPE === 'postgres') {
await pool.query('DELETE FROM email_templates WHERE id = $1', [req.params.id]); await pool.query('DELETE FROM email_templates WHERE id = $1', [req.params.id]);
} else { } else {
@@ -216,16 +198,15 @@ app.delete('/api/templates/:id', async (req, res) => {
} }
res.json({ success: true }); res.json({ success: true });
} catch (err) { } catch (err) {
console.error(err); console.error("Delete Error:", err);
res.status(500).json({ error: 'Failed to delete template' }); res.status(500).json({ error: 'Failed to delete template' });
} }
}); });
// Handle React Routing
app.get('*', (req, res) => { app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html')); res.sendFile(path.join(__dirname, 'dist', 'index.html'));
}); });
app.listen(PORT, () => { app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`); console.log(`Server running on port ${PORT}`);
}); });