Compare commits
4 Commits
b8891e2d11
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 32e25d9413 | |||
|
|
b9d7ff0e1d | ||
| 02dbe4b11e | |||
|
|
bf40149c0e |
181
server.js
181
server.js
@@ -18,7 +18,6 @@ const pdfParse = require('pdf-parse'); // For PDF files
|
||||
const ExcelJS = require('exceljs'); // For Excel files
|
||||
|
||||
// Markdown and HTML processing
|
||||
const { marked } = require('marked');
|
||||
const createDOMPurify = require('dompurify');
|
||||
const { JSDOM } = require('jsdom');
|
||||
|
||||
@@ -26,6 +25,16 @@ const { JSDOM } = require('jsdom');
|
||||
const window = new JSDOM('').window;
|
||||
const DOMPurify = createDOMPurify(window);
|
||||
|
||||
// Dynamic import for marked (ES module)
|
||||
let marked = null;
|
||||
async function initializeMarked() {
|
||||
if (!marked) {
|
||||
const markedModule = await import('marked');
|
||||
marked = markedModule.marked;
|
||||
}
|
||||
return marked;
|
||||
}
|
||||
|
||||
// Helper function to extract text from various document formats
|
||||
async function extractTextFromDocument(filePath, fileExtension) {
|
||||
try {
|
||||
@@ -152,10 +161,16 @@ app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.static('public'));
|
||||
app.use(session({
|
||||
secret: process.env.SESSION_SECRET || 'educat-secret-key',
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: { secure: false, maxAge: 24 * 60 * 60 * 1000 } // 24 hours
|
||||
secret: process.env.SESSION_SECRET || 'educat-secret-key-2025',
|
||||
resave: true,
|
||||
saveUninitialized: true,
|
||||
rolling: true,
|
||||
cookie: {
|
||||
secure: false,
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days instead of 1 day
|
||||
httpOnly: true
|
||||
},
|
||||
name: 'educat.session.id' // Custom session name
|
||||
}));
|
||||
app.use(flash());
|
||||
|
||||
@@ -768,23 +783,24 @@ app.get('/dashboard', requireAuth, async (req, res) => {
|
||||
// Chat route
|
||||
app.get('/chat', requireAuth, async (req, res) => {
|
||||
try {
|
||||
// Load user chat history
|
||||
let chatHistory = [];
|
||||
try {
|
||||
chatHistory = await loadChatHistory(req.session.userId);
|
||||
} catch (error) {
|
||||
console.error('Error loading chat history:', error);
|
||||
chatHistory = [];
|
||||
// Initialize chat session ID if it doesn't exist
|
||||
if (!req.session.chatSessionId) {
|
||||
req.session.chatSessionId = `educat-${req.session.userId}-${Date.now()}`;
|
||||
}
|
||||
|
||||
// Initialize chat history in session if it doesn't exist
|
||||
if (!req.session.chatHistory) {
|
||||
req.session.chatHistory = [];
|
||||
}
|
||||
|
||||
res.render('chat', {
|
||||
title: 'AI Chat - EduCat',
|
||||
chatHistory: chatHistory
|
||||
title: 'Chat with EduCat AI',
|
||||
chatHistory: req.session.chatHistory
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Chat route error:', error);
|
||||
res.render('chat', {
|
||||
title: 'AI Chat - EduCat',
|
||||
title: 'Chat with EduCat AI',
|
||||
chatHistory: []
|
||||
});
|
||||
}
|
||||
@@ -1092,41 +1108,54 @@ app.post('/api/chat', requireAuth, async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Load existing chat history from storage
|
||||
let existingHistory = [];
|
||||
try {
|
||||
existingHistory = await loadChatHistory(req.session.userId);
|
||||
} catch (error) {
|
||||
console.log('No existing chat history found, starting fresh');
|
||||
// Initialize chat history in session if it doesn't exist
|
||||
if (!req.session.chatHistory) {
|
||||
req.session.chatHistory = [];
|
||||
}
|
||||
|
||||
// Prepare history for API call (last 10 conversations)
|
||||
const recentHistory = existingHistory.slice(-10).map(conv => [
|
||||
{ role: 'human', content: conv.human },
|
||||
{ role: 'ai', content: conv.ai }
|
||||
]).flat();
|
||||
|
||||
// Call Flowise API for chat
|
||||
const response = await axios.post(`${FLOWISE_API_URL}/${FLOWISE_CHATFLOW_ID}`, {
|
||||
question: message.trim(),
|
||||
history: recentHistory
|
||||
});
|
||||
|
||||
const botResponse = response.data.text || response.data.answer || 'Sorry, I could not process your request.';
|
||||
|
||||
// Save the conversation to history
|
||||
const conversation = {
|
||||
human: message.trim(),
|
||||
ai: botResponse,
|
||||
timestamp: new Date().toISOString()
|
||||
|
||||
// Initialize or get persistent chat session ID for this user
|
||||
if (!req.session.chatSessionId) {
|
||||
req.session.chatSessionId = `${req.session.userId}-${Date.now()}`;
|
||||
}
|
||||
|
||||
// Prepare the request payload for Flowise with sessionId and chatId
|
||||
const flowisePayload = {
|
||||
question: message,
|
||||
history: req.session.chatHistory,
|
||||
sessionId: req.session.chatSessionId
|
||||
};
|
||||
|
||||
existingHistory.push(conversation);
|
||||
await saveChatHistory(req.session.userId, existingHistory);
|
||||
|
||||
// Add chatId if we have one from previous conversations
|
||||
if (req.session.chatId) {
|
||||
flowisePayload.chatId = req.session.chatId;
|
||||
}
|
||||
|
||||
// Call Flowise API for chat with session history and sessionId
|
||||
const response = await axios.post(`${FLOWISE_API_URL}/${FLOWISE_CHATFLOW_ID}`, flowisePayload);
|
||||
const aiResponse = response.data.text || response.data.answer || 'No response received';
|
||||
|
||||
// Save the chatId from Flowise response for future requests
|
||||
if (response.data.chatId) {
|
||||
req.session.chatId = response.data.chatId;
|
||||
}
|
||||
|
||||
// Add the conversation to session history
|
||||
req.session.chatHistory.push({
|
||||
human: message,
|
||||
ai: aiResponse,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Save session
|
||||
req.session.save((err) => {
|
||||
if (err) {
|
||||
console.error('Error saving session:', err);
|
||||
}
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
response: botResponse
|
||||
response: aiResponse
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Chat error:', error);
|
||||
@@ -1138,13 +1167,47 @@ app.post('/api/chat', requireAuth, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Delete chat history endpoint
|
||||
app.delete('/api/chat/history', requireAuth, async (req, res) => {
|
||||
// Get chat history endpoint
|
||||
app.get('/api/chat/history', requireAuth, (req, res) => {
|
||||
try {
|
||||
await clearChatHistory(req.session.userId);
|
||||
const chatHistory = req.session.chatHistory || [];
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Chat history cleared successfully'
|
||||
chatHistory: chatHistory
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error getting chat history:', error);
|
||||
res.json({
|
||||
success: false,
|
||||
error: 'Failed to get chat history',
|
||||
details: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Delete chat history endpoint
|
||||
app.delete('/api/chat/history', requireAuth, (req, res) => {
|
||||
try {
|
||||
req.session.chatHistory = [];
|
||||
// Reset the session ID to start a fresh conversation
|
||||
req.session.chatSessionId = `${req.session.userId}-${Date.now()}`;
|
||||
// Clear the Flowise chatId
|
||||
delete req.session.chatId;
|
||||
|
||||
req.session.save((err) => {
|
||||
if (err) {
|
||||
console.error('Error clearing chat session:', err);
|
||||
return res.json({
|
||||
success: false,
|
||||
error: 'Failed to clear chat history'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Chat history cleared'
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error clearing chat history:', error);
|
||||
@@ -1454,7 +1517,7 @@ app.post('/api/render-revised-content', requireAuth, async (req, res) => {
|
||||
case 'html':
|
||||
if (isMarkdownContent || autoDetect === false) {
|
||||
// Convert markdown to safe HTML
|
||||
renderedContent = markdownToSafeHtml(content);
|
||||
renderedContent = await markdownToSafeHtml(content);
|
||||
} else {
|
||||
// Just escape HTML and preserve line breaks for plain text
|
||||
renderedContent = escapeHtml(content).replace(/\n/g, '<br>');
|
||||
@@ -2705,10 +2768,13 @@ app.listen(PORT, () => {
|
||||
});
|
||||
|
||||
// Helper function to convert markdown to safe HTML
|
||||
function markdownToSafeHtml(markdownText) {
|
||||
async function markdownToSafeHtml(markdownText) {
|
||||
try {
|
||||
// Initialize marked with dynamic import
|
||||
const markedInstance = await initializeMarked();
|
||||
|
||||
// Configure marked options for better security and features
|
||||
marked.setOptions({
|
||||
markedInstance.setOptions({
|
||||
gfm: true, // GitHub Flavored Markdown
|
||||
breaks: true, // Convert line breaks to <br>
|
||||
sanitize: false, // We'll use DOMPurify instead for better control
|
||||
@@ -2718,7 +2784,7 @@ function markdownToSafeHtml(markdownText) {
|
||||
});
|
||||
|
||||
// Convert markdown to HTML
|
||||
const rawHtml = marked.parse(markdownText);
|
||||
const rawHtml = markedInstance.parse(markdownText);
|
||||
|
||||
// Sanitize the HTML with DOMPurify
|
||||
const cleanHtml = DOMPurify.sanitize(rawHtml, {
|
||||
@@ -2793,9 +2859,11 @@ async function ensureChatHistoryDirectory() {
|
||||
// Save chat history to persistent storage
|
||||
async function saveChatHistory(userId, chatHistory) {
|
||||
try {
|
||||
console.log(`Saving chat history for user ${userId}, ${chatHistory.length} messages`);
|
||||
await ensureChatHistoryDirectory();
|
||||
const historyPath = path.join(CHAT_HISTORY_DIR, `chat-${userId}.json`);
|
||||
await fs.writeJSON(historyPath, chatHistory, { spaces: 2 });
|
||||
console.log(`Chat history saved successfully to ${historyPath}`);
|
||||
} catch (error) {
|
||||
console.error('Error saving chat history:', error);
|
||||
throw error;
|
||||
@@ -2805,12 +2873,16 @@ async function saveChatHistory(userId, chatHistory) {
|
||||
// Load chat history from persistent storage
|
||||
async function loadChatHistory(userId) {
|
||||
try {
|
||||
console.log(`Loading chat history for user ${userId}`);
|
||||
await ensureChatHistoryDirectory();
|
||||
const historyPath = path.join(CHAT_HISTORY_DIR, `chat-${userId}.json`);
|
||||
|
||||
if (await fs.pathExists(historyPath)) {
|
||||
return await fs.readJSON(historyPath);
|
||||
const history = await fs.readJSON(historyPath);
|
||||
console.log(`Loaded ${history.length} chat messages for user ${userId}`);
|
||||
return history;
|
||||
} else {
|
||||
console.log(`No chat history file found for user ${userId}`);
|
||||
return [];
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -2844,6 +2916,7 @@ async function initializeDataDirectories() {
|
||||
|
||||
// Call initialization
|
||||
initializeDataDirectories().catch(console.error);
|
||||
initializeDataDirectories().catch(console.error);
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user