- sistemato timeout corto

- corretto errori sulla generazione del PDF
- corretto alcune directory
- corretto fs.promise
- corretto CORS !
This commit is contained in:
Surya Paolo
2025-05-15 14:27:46 +02:00
parent a76d6c9b12
commit 768d299881
10 changed files with 402 additions and 383 deletions

View File

@@ -13,7 +13,7 @@ const _ = require('lodash');
const cors = require('cors');
// console.log(" 2) fs");
const fs = require('fs');
const fs = require('fs'); // 👈 Usa il modulo promises
var https = require('https');
var http = require('http');
@@ -136,6 +136,7 @@ connectToDatabase(connectionUrl, options)
const mygen_router = require('./router/mygen_router');
const aitools_router = require('./router/aitools_router');
const article_router = require('./router/articleRoutes');
const api_router = require('./router/api_router');
const { MyEvent } = require('./models/myevent');
@@ -245,6 +246,7 @@ connectToDatabase(connectionUrl, options)
app.use('/mygen', mygen_router);
app.use('/aitools', aitools_router);
app.use('/apisqlsrv', article_router);
app.use('/api', api_router);
mystart();
});
@@ -531,16 +533,16 @@ connectToDatabase(connectionUrl, options)
try {
// console.log('checkdir', folderprof);
if (!fs.existsSync(folderprof)) {
if (!tools.existsSync(folderprof)) {
console.log('*** Creadir', folderprof);
fs.mkdirSync(folderprof);
await fs.mkdirSync(folderprof);
}
folderprof = dir + 'profile/' + myuser.username + '/' + table;
// console.log('checkdir', folderprof);
if (!fs.existsSync(folderprof)) {
if (!tools.existsSync(folderprof)) {
console.log('creadir', folderprof);
fs.mkdirSync(folderprof);
await fs.mkdirSync(folderprof);
}
} catch (e) {}
}
@@ -671,383 +673,352 @@ connectToDatabase(connectionUrl, options)
}
}
// Funzione migliorata per ottenere chiave e certificato
function getCredentials(hostname) {
if (NUOVO_METODO_TEST) {
if (METODO_MULTI_CORS) {
const fileprivkey = `/etc/letsencrypt/live/${hostname}/` + process.env.PATH_CERT_KEY;
const filecert = `/etc/letsencrypt/live/${hostname}/` + process.env.PATH_SERVER_CRT;
try {
let keyPath, certPath;
console.log('fileprivkey: ', fileprivkey, ' filecert: ', filecert);
/* return {
SNICallback: function (hostname, callback) {
console.log('hostname: ', hostname);
if (domains.includes(hostname)) {
const fileprivkey = `/etc/letsencrypt/live/${hostname}/privkey.pem`;
const filecert = `/etc/letsencrypt/live/${hostname}/fullchain.pem`;
// console.log('fileprivkey: ', fileprivkey, ' filecert: ', filecert);
const domainCert = {
key: fs.readFileSync(fileprivkey, "utf8"),
cert: fs.readFileSync(filecert, "utf8"),
};
callback(null, domainCert);
} else {
callback(null, { key: privateKey, cert: certificate });
}
}
};*/
try {
const key = fs.readFileSync(fileprivkey, 'utf8');
const cert = fs.readFileSync(filecert, 'utf8');
return { key, cert };
} catch (error) {
console.error(`Errore nel caricamento delle credenziali per ${hostname}:`, error);
// Gestisci l'errore, per esempio ritorna null o lancia un'eccezione
if (NUOVO_METODO_TEST) {
if (METODO_MULTI_CORS) {
// Percorso basato su hostname (Let's Encrypt)
keyPath = `/etc/letsencrypt/live/${hostname}/${process.env.PATH_CERT_KEY}`;
certPath = `/etc/letsencrypt/live/${hostname}/${process.env.PATH_SERVER_CRT}`;
} else {
// Percorso relativo
keyPath = path.resolve(`./${process.env.PATH_CERT_KEY}`);
certPath = path.resolve(`./${process.env.PATH_SERVER_CRT}`);
}
// Verifica esistenza file
if (!tools.existsSync(keyPath)) {
throw new Error(`Chiave privata non trovata: ${keyPath}`);
}
if (!tools.existsSync(certPath)) {
throw new Error(`Certificato non trovato: ${certPath}`);
}
// Leggi chiave e certificato
const key = fs.readFileSync(keyPath, 'utf8');
const cert = fs.readFileSync(certPath, 'utf8');
// Restituisci oggetto con credenziali + configurazione TLS avanzata
return {
key,
cert,
ciphers:
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384',
honorCipherOrder: true,
secureOptions: require('constants').OPENSSL_OPTIONS_TLS_NODELAY,
secureProtocol: 'TLSv1_2_method', // Forza TLS 1.2+ (meglio se usi TLSv1_3)
};
} else {
const keyStream = path.resolve(`./${process.env.PATH_CERT_KEY}`);
const certificateStream = path.resolve(`./${process.env.PATH_SERVER_CRT}`);
// Metodo legacy (opzionale)
throw new Error('Metodo legacy non supportato');
}
} catch (error) {
console.error(`[getCredentials] Errore per ${hostname}:`, error.message);
throw error;
}
}
// 🔧 Funzione factory per creare e configurare un server HTTPS
function createHttpsServer({ hostname, port, website, app, credentials, timeoutMinutes = 5 }) {
const server = https.createServer(credentials, app);
const privateKey = fs.readFileSync(keyStream, 'utf8');
const certificate = fs.readFileSync(certificateStream, 'utf8');
const timeoutMs = 1000 * 60 * timeoutMinutes;
return { key: privateKey, cert: certificate };
// ⏱️ Timeout globale per la connessione TCP
server.setTimeout(timeoutMs, () => {
console.log(`TCP timeout su server: ${hostname}:${port}`);
});
// ⏱️ Timeout per singola richiesta HTTP
server.on('request', (req, res) => {
req.setTimeout(timeoutMs);
res.setTimeout(timeoutMs);
});
// 📡 Eventuali altri eventi utili
server.on('clientError', (err, socket) => {
console.error(`Client error su ${hostname}:${port}:`, err.message);
});
// Avvia il server
server.listen(port, () => {
console.log(
'⭐️⭐️⭐️⭐️⭐️ HTTPS server: %s Port: %d%s',
hostname,
port,
website ? ' WebSite = ' + website : ''
);
});
return server;
}
function parseDomains() {
let domains = [];
let domainsAllowed = [];
try {
if (process.env.DOMAINS) domains = JSON.parse(process.env.DOMAINS);
if (process.env.DOMAINS_ALLOWED) domainsAllowed = JSON.parse(process.env.DOMAINS_ALLOWED);
} catch (error) {
console.error('Errore parsing DOMAINS:', error);
}
return { domains, domainsAllowed };
}
function buildAllowedOrigins(domains, domainsAllowed, isProduction) {
if (!isProduction) {
return ['https://localhost:3000', 'https://localhost:8089', 'https://localhost:8084', 'https://localhost:8088'];
}
const baseOrigins = domains.flatMap((domain) => [
`https://${domain.hostname}`,
`https://api.${domain.hostname}`,
`https://test.${domain.hostname}`,
`https://testapi.${domain.hostname}`,
`http://${domain.hostname}`,
`http://api.${domain.hostname}`,
`http://test.${domain.hostname}`,
`http://testapi.${domain.hostname}`,
]);
const allowedExtra = domainsAllowed.flatMap((domain) => [`https://${domain}`, `http://${domain}`]);
return [...baseOrigins, ...allowedExtra];
}
function createCorsOptions(domains, domainsAllowed, isProduction, noCors = false) {
if (noCors) {
console.log('NOCORS mode enabled');
return {
exposedHeaders: ['x-auth', 'x-refrtok'],
};
}
const allowedOrigins = buildAllowedOrigins(domains, domainsAllowed, isProduction);
const originValidator = (origin, callback) => {
if (!origin) {
console.log('✅ Origin undefined or empty — allowing');
return callback(null, true);
}
if (typeof origin !== 'string' || !/^https?:\/\/[^\s/$.?#].[^\s]*$/.test(origin)) {
console.error('❌ Invalid origin:', origin);
return callback(new Error('Origine non valida'), false);
}
if (allowedOrigins.includes(origin)) {
return callback(null, true);
}
console.warn('❌ Origin blocked:', origin);
return callback(new Error('CORS non permesso per questa origine'), false);
};
return {
origin: originValidator,
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
allowedHeaders: [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Authorization',
'x-auth',
'x-refrtok',
],
exposedHeaders: ['x-auth', 'x-refrtok'],
maxAge: 86400,
preflightContinue: false,
optionsSuccessStatus: 204,
};
}
function setupMiddleware(app, corsOptions, isDebug = false) {
app.use(cors(corsOptions));
app.use(express.json());
app.options('*', cors(corsOptions));
if (isDebug) {
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
console.log('Request Headers:', req.headers);
const oldSend = res.send;
res.send = function (...args) {
console.log('Response Headers:', res.getHeaders());
return oldSend.apply(res, args);
};
next();
});
}
app.use((err, req, res, next) => {
if (err.message === 'CORS non permesso per questa origine') {
console.error('❌ Errore CORS:', {
origin: req.headers.origin,
method: req.method,
path: req.path,
});
return res.status(403).json({
error: `❌ CORS non permesso per questa origine (${req.headers.origin})`,
origin: req.headers.origin,
});
}
next(err);
});
}
function createHttpOrHttpsServer(app, port, isProduction, domains) {
if (isProduction) {
domains.forEach((domain) => {
const credentials = getCredentials(domain.hostname);
createHttpsServer({
hostname: domain.hostname,
port: domain.port,
website: domain.website,
app,
credentials,
timeoutMinutes: 6,
});
});
return null;
}
if (process.env.HTTPS_LOCALHOST === 'true') {
try {
const key = fs.readFileSync(path.resolve(`./${process.env.PATH_CERT_KEY}`), 'utf8');
const cert = fs.readFileSync(path.resolve(`./${process.env.PATH_SERVER_CRT}`), 'utf8');
const credentials = {
key,
cert,
ciphers:
'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384',
honorCipherOrder: true,
secureProtocol: 'TLSv1_2_method',
};
const httpsServer = https.createServer(credentials, app);
httpsServer.listen(port);
console.log('⭐️ HTTPS server running locally on port', port);
return httpsServer;
} catch (error) {
console.error('Errore caricamento certificati HTTPS locali:', error.message);
// fallback HTTP server
}
}
// Caso di default non specificato, potrebbe essere necessario aggiungere una gestione degli errori qui
const httpServer = http.createServer(app);
httpServer.listen(port);
console.log('⭐️ HTTP server running on port', port);
return httpServer;
}
function setupWebSocketServer(httpOrHttpsServer) {
if (!httpOrHttpsServer) {
console.error('Nessun server HTTP o HTTPS disponibile per WebSocket');
return null;
}
const wss = new WebSocket.Server({ server: httpOrHttpsServer });
wss.on('connection', (ws) => {
console.log('Client socket connected...');
const { User } = require('./models/user');
const pty = require('node-pty');
let scriptProcess = null;
ws.on('message', (message) => {
try {
const parsed = JSON.parse(message);
if (parsed.type === 'start_script' && User.isAdminById(parsed.user_id)) {
if (scriptProcess) scriptProcess.kill();
const scriptPath = path.join(__dirname, '..', '..', parsed.scriptName);
if (!tools.existsSync(scriptPath)) {
return ws.send(JSON.stringify({ type: 'error', data: 'Script non trovato o non autorizzato' }));
}
scriptProcess = pty.spawn('bash', [scriptPath], {
name: 'xterm-color',
cols: 80,
rows: 40,
cwd: process.cwd(),
env: process.env,
});
let buffer = '';
scriptProcess.on('data', (data) => {
buffer += data;
ws.send(JSON.stringify({ type: 'output', data }));
if (
buffer.endsWith(': ') ||
buffer.includes('? ') ||
buffer.toLowerCase().includes('password') ||
buffer.includes('Inserisci') ||
buffer.includes('Inserted') ||
buffer.includes('(Y)')
) {
ws.send(JSON.stringify({ type: 'input_required', prompt: data.trim() }));
buffer = '';
}
if (buffer.length > 5024) buffer = buffer.slice(-500);
});
scriptProcess.on('exit', (code) => {
const closeMsg = code === 0 ? '*** FINE SCRIPT ***' : `Script terminato con codice ${code}`;
ws.send(JSON.stringify({ type: 'close', data: closeMsg }));
});
} else if (parsed.type === 'input') {
if (scriptProcess) {
scriptProcess.write(parsed.data + '\n');
}
}
} catch (err) {
console.error("Errore durante l'elaborazione del messaggio:", err.message);
}
});
ws.on('close', () => {
console.log('*** Client socket disconnected');
if (scriptProcess) scriptProcess.kill();
});
});
return wss;
}
function startServer(app, port) {
try {
const isProduction = ['production', 'test'].includes(process.env.NODE_ENV);
let domains = [];
let domains_allowed = [];
try {
if (process.env.DOMAINS) domains = JSON.parse(process.env.DOMAINS);
if (process.env.DOMAINS_ALLOWED) domains_allowed = JSON.parse(process.env.DOMAINS_ALLOWED);
} catch (error) {
console.error('Errore durante la conversione della stringa DOMAINS:', error);
}
console.log('domains', domains);
let httpsServer = null;
let httpServer = null;
console.log('isProduction', isProduction);
const ISDEBUG = false;
const NOCORS = false;
const ISDEBUG = false;
const { domains, domainsAllowed } = parseDomains();
let corsOptions = {};
console.log('domains:', domains);
console.log('isProduction:', isProduction);
if (NOCORS) {
console.log('NOCORS');
corsOptions = {
exposedHeaders: ['x-auth', 'x-refrtok'], // Intestazioni da esporre al client
};
} else {
console.log('WITH CORS');
let credentials = true;
const corsOptions = createCorsOptions(domains, domainsAllowed, isProduction, NOCORS);
let allowedOrigins = null;
setupMiddleware(app, corsOptions, ISDEBUG);
if (!isProduction) {
allowedOrigins = 'https://localhost:3000';
} else {
allowedOrigins = domains.flatMap((domain) => [
`https://${domain.hostname}`,
`https://api.${domain.hostname}`,
`https://test.${domain.hostname}`,
`https://testapi.${domain.hostname}`,
`http://${domain.hostname}`,
`http://api.${domain.hostname}`,
`http://test.${domain.hostname}`,
`http://testapi.${domain.hostname}`,
]);
const server = createHttpOrHttpsServer(app, port, isProduction, domains);
// Aggiungi i domini da DOMAINS_ALLOWED
allowedOrigins = allowedOrigins.concat(
domains_allowed.map((domain) => [`https://${domain}`, `http://${domain}`]).flat()
);
}
const wss = setupWebSocketServer(server);
console.log('allowedOrigins', allowedOrigins);
let myorigin = '*';
if (domains.length > 0) {
myorigin = (origin, callback) => {
try {
// Validazione dell'input
if (origin === undefined) {
console.log('✅ Origin UNDEFINED... vado avanti lo stesso !');
return callback(null, true);
}
if (!origin || typeof origin !== 'string' || !/^https?:\/\/[^\s/$.?#].[^\s]*$/.test(origin)) {
console.error('❌ Origine non valida:', origin);
return callback(new Error('Origine non valida'), false);
}
// Controllo delle origini consentite
if (allowedOrigins.includes(origin)) {
// console.log('✅ Origine consentita:', origin);
return callback(null, true);
}
// Blocco delle origini non autorizzate
console.warn('❌ Origine bloccata:', origin);
return callback(new Error('CORS non permesso per questa origine'), false);
} catch (error) {
console.error("Errore durante la verifica dell'origine:", error.message);
return callback(error, false);
}
};
}
// Configurazione CORS dettagliata
const corsOptions = {
origin: myorigin,
credentials,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
allowedHeaders: [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Authorization',
'x-auth',
'x-refrtok',
],
exposedHeaders: ['x-auth', 'x-refrtok'],
maxAge: 86400, // Preflight cache 24 ore
preflightContinue: false,
optionsSuccessStatus: 204,
};
// Applica CORS come primo middleware
app.use(cors(corsOptions));
// HO AGGIUNTO QUESTA RIGA PER IL CORS !!!!!!!
app.use(express.json()); // Middleware per il parsing del corpo JSON
app.options('*', cors(corsOptions)); // Gestisce tutte le richieste OPTIONS
// Middleware personalizzato per assicurare gli headers CORS
/*app.use((req, res, next) => {
const origin = req.headers.origin || '*';
if (allowedOrigins.includes(origin) || corsOptions.origin === '*') {
// console.log(' ... ORIGIN', origin);
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Expose-Headers', 'x-auth, x-refrtok');
}
next();
});*/
// Log middleware per debug
app.use((req, res, next) => {
if (ISDEBUG) {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
console.log('Request Headers:', req.headers);
}
// Intercetta la risposta per loggare gli headers
const oldSend = res.send;
res.send = function (...args) {
if (ISDEBUG) {
console.log('Response Headers:', res.getHeaders());
}
return oldSend.apply(res, args);
};
next();
});
// Gestione errori CORS
app.use((err, req, res, next) => {
if (err.message === 'CORS non permesso per questa origine') {
console.error('❌ Errore CORS:', {
origin: req.headers.origin,
method: req.method,
path: req.path,
});
res.status(403).json({
error: '❌ CORS non permesso per questa origine (' + req.headers.origin + ')',
origin: req.headers.origin,
});
} else {
next(err);
}
});
}
if (isProduction) {
for (let i = 0; i < domains.length; i++) {
const mycredentials = getCredentials(domains[i].hostname);
// console.log('credentials: ', credentials);
httpsServer = https.createServer(mycredentials, app);
console.log(
'⭐️⭐️⭐️⭐️⭐️ HTTPS server: ' + domains[i].hostname + ' Port:',
domains[i].port + (domains[i].website ? 'WebSite = ' + domains[i].website : '')
);
httpsServer.listen(domains[i].port);
}
} else {
if (process.env.HTTPS_LOCALHOST === 'true') {
let mycredentials = null;
try {
const keyStream = path.resolve(`./${process.env.PATH_CERT_KEY}`);
const certificateStream = path.resolve(`./${process.env.PATH_SERVER_CRT}`);
const privateKey = fs.readFileSync(keyStream, 'utf8');
const certificate = fs.readFileSync(certificateStream, 'utf8');
mycredentials = {
key: privateKey,
cert: certificate,
ciphers:
'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES256-SHA384',
honorCipherOrder: true,
secureProtocol: 'TLSv1_2_method',
};
} catch (error) {
console.error('Errore durante la lettura dei file di certificazione, error:', error.message);
throw error;
}
if (mycredentials) {
httpsServer = https.createServer(mycredentials, app);
console.log('⭐️⭐️⭐️ HTTPS server IN LOCALE : port', port);
httpsServer.listen(port);
} else {
httpServer = http.createServer(app);
if (httpServer) {
console.log('⭐️⭐️⭐️ HTTP server IN LOCALE : port', port);
httpServer.listen(port);
}
}
// console.log('credentials', credentials);
} else {
httpServer = http.createServer(app);
if (httpServer) {
console.log('⭐️⭐️⭐️ HTTP server IN LOCALE : port', port);
httpServer.listen(port);
}
}
}
let wss = null;
if (httpsServer) {
wss = new WebSocket.Server({ server: httpsServer });
} else if (httpServer) {
wss = new WebSocket.Server({ server: httpServer });
} else {
// console.error('Nessun server HTTP o HTTPS disponibile per WebSocket');
// process.exit(1);
}
if (wss) {
wss.on('connection', (ws) => {
console.log('Client socket connected...');
const { User } = require('./models/user');
let scriptProcess = null;
const pty = require('node-pty');
ws.on('message', (message) => {
const parsedMessage = JSON.parse(message);
try {
if (parsedMessage.type === 'start_script' && User.isAdminById(parsedMessage.user_id)) {
if (scriptProcess) {
scriptProcess.kill();
}
const scriptPath = path.join(__dirname, '..', '..', '', parsedMessage.scriptName);
// Verifica che lo script esista e sia all'interno della directory consentita
if (fs.existsSync(scriptPath)) {
scriptProcess = pty.spawn('bash', [scriptPath], {
name: 'xterm-color',
cols: 80,
rows: 40,
cwd: process.cwd(),
env: process.env,
});
let buffer = '';
scriptProcess.on('data', (data) => {
buffer += data;
// Invia l'output al client
ws.send(JSON.stringify({ type: 'output', data: data }));
// Controlla se c'è una richiesta di input
if (
buffer &&
(buffer.endsWith(': ') ||
buffer.includes('? ') ||
buffer.toLowerCase().includes('password') ||
buffer.includes('Inserisci') ||
buffer.includes('Inserted') ||
buffer.includes('(Y'))
) {
ws.send(JSON.stringify({ type: 'input_required', prompt: data.trim() }));
buffer = '';
}
// Pulisci il buffer se diventa troppo grande
if (buffer.length > 5024) {
buffer = buffer.slice(-500);
}
});
scriptProcess.on('exit', (code) => {
if (code === 0) {
ws.send(JSON.stringify({ type: 'close', data: `*** FINE SCRIPT ***` }));
} else {
ws.send(JSON.stringify({ type: 'close', data: `Script terminato con codice ${code}` }));
}
});
} else {
ws.send(JSON.stringify({ type: 'error', data: 'Script non trovato o non autorizzato' }));
}
} else if (parsedMessage.type === 'input') {
if (scriptProcess) {
scriptProcess.write(parsedMessage.data + '\n');
}
}
} catch (error) {
console.error("Errore durante l'elaborazione del messaggio:", error.message);
}
});
ws.on('close', () => {
console.log('*** Client socket disconnected');
if (scriptProcess) {
scriptProcess.kill();
}
});
});
} else {
console.error('Nessuna Socket Aperta con WebSocket !!');
}
} catch (e) {
console.log('error startServer: ' + e);
return { server, wss };
} catch (error) {
console.error('Errore in startServer:', error);
}
}
})