HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/ttassist/app.js
const express = require('express');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const router = express.Router();
require('dotenv').config();

const openai = new(require('openai'))();

// Carica variabili d'ambiente
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
const ASSISTANT_ID = process.env.ASSISTANT_ID;
const MODEL = process.env.MODEL;
const MAX_MESSAGES = process.env.MAX_MESSAGES ? parseInt(process.env.MAX_MESSAGES) : 10;

// Configura il CORS per accettare richieste dal dominio frontend specifico
const corsOptions = {
	origin: '*',
	methods: 'GET,POST,PUT,DELETE,OPTIONS',
	optionsSuccessStatus: 200,
	credentials: true
};
router.use(cors(corsOptions));

// Configura il rate limiter
const limiter = rateLimit({
	windowMs: 15 * 60 * 1000,
	max: 100
});
router.use(limiter);

router.use(express.json()); // Middleware per il parsing JSON

// Inizializza la cronologia delle conversazioni e il timer di timeout
let conversationHistory = [];
let resetTimeout;

// Funzione per resettare la cronologia dopo un periodo di inattività
function resetConversation() {
	console.log("Reset della cronologia per inattività.");
	conversationHistory = [];
}

// Funzione per limitare la cronologia al numero massimo di messaggi configurato
function limitConversationHistory() {
	if (conversationHistory.length > MAX_MESSAGES) {
		conversationHistory = conversationHistory.slice(-MAX_MESSAGES);
	}
}

// Funzione per rimuovere i riferimenti alle fonti dal testo
function removeSources(text) {
	return text.replace(/【\d+:\d+†[^\]]*】/g, '');
}

// Rotta principale /ask
router.post('/ask', async (req, res) => {
	//    console.log('Richiesta ricevuta su /ask');
	console.log('Richiesta:', req.body);

	const {
		message
	} = req.body;

	// Reset del timer di inattività ogni volta che riceviamo un nuovo messaggio
	clearTimeout(resetTimeout);
	resetTimeout = setTimeout(resetConversation, 30 * 60 * 1000);

	try {
		conversationHistory.push({
			role: 'user',
			content: message
		});
		limitConversationHistory();

		const thread = await openai.beta.threads.create();
		console.log("Thread creato con ID:", thread.id);

		for (const msg of conversationHistory) {
			await openai.beta.threads.messages.create(thread.id, {
				role: msg.role,
				content: msg.content
			});
		}

		const run = openai.beta.threads.runs.stream(thread.id, {
			assistant_id: ASSISTANT_ID,
			model: MODEL
		});

		let assistantResponse = '';

		run
			.on('textDelta', (textDelta) => {
				assistantResponse += textDelta.value;
			})
			.on('end', () => {
				assistantResponse = removeSources(assistantResponse);
				console.log("Risposta:", assistantResponse);

				conversationHistory.push({
					role: 'assistant',
					content: assistantResponse
				});
				limitConversationHistory();

				res.json({
					response: assistantResponse
				});
			})
			.on('error', (error) => {
				console.error('Errore nello streaming della risposta:', error);
				res.status(500).json({
					error: 'Errore nella comunicazione con l\'Assistant'
				});
			});

	} catch (error) {
		res.status(500).json({
			error: 'Cannot communicate with the TTA Assistant'
		});
	}
});

//console.log('Router configurato correttamente');
module.exports = router; // Esporta solo il router