From b2b12a419780c328485ae1418b44cebe3ea1e362 Mon Sep 17 00:00:00 2001 From: inubimambo Date: Sun, 6 Jul 2025 23:39:32 +0800 Subject: [PATCH] Fix upload 'processing' status --- server.js | 182 ++++++++++++++++++++++++++++++++++++++------ views/chat.ejs | 104 +++++++++++++++++++------ views/dashboard.ejs | 110 ++++++++++++++++++++++---- 3 files changed, 335 insertions(+), 61 deletions(-) diff --git a/server.js b/server.js index f87576f..67194d0 100644 --- a/server.js +++ b/server.js @@ -634,7 +634,7 @@ app.post('/upload', requireAuth, upload.single('noteFile'), async (req, res) => console.log('Response sent, starting async processing...'); // Process document asynchronously - processDocumentAsync(fileInfo, req.session.userId); + processDocumentAsync(fileInfo, req.session.userId, req.session); } catch (error) { console.error('Upload error:', error); @@ -647,13 +647,24 @@ app.post('/upload', requireAuth, upload.single('noteFile'), async (req, res) => }); // Async function to process document and upsert to Flowise using FormData -async function processDocumentAsync(fileInfo, userId) { +async function processDocumentAsync(fileInfo, userId, session) { try { console.log('=== DOCUMENT PROCESSING START (FormData) ==='); console.log(`Starting document processing for: ${fileInfo.originalName}`); console.log(`File path: ${fileInfo.path}`); console.log(`File size: ${fileInfo.size} bytes`); console.log(`User ID: ${userId}`); + console.log(`Session ID in processing: ${session.id || 'undefined'}`); + + // Find the file in the session to update it by reference + const sessionFile = session.uploadedFiles.find(f => f.id === fileInfo.id); + if (!sessionFile) { + console.error('File not found in session:', fileInfo.id); + console.error('Available files in session:', session.uploadedFiles.map(f => ({ id: f.id, name: f.originalName }))); + return; + } + + console.log('Found session file for update:', { id: sessionFile.id, name: sessionFile.originalName, currentStatus: sessionFile.status }); // Create document metadata const documentMetadata = { @@ -668,7 +679,7 @@ async function processDocumentAsync(fileInfo, userId) { console.log('Document metadata:', documentMetadata); // Initialize processing result - fileInfo.processingResult = { + sessionFile.processingResult = { totalChunks: 1, // FormData uploads are single operations successfulChunks: 0, failedChunks: 0, @@ -683,41 +694,71 @@ async function processDocumentAsync(fileInfo, userId) { console.log('FormData upsert result:', upsertResult); - // Update file info with processing results - fileInfo.status = upsertResult.success ? 'processed' : 'failed'; - fileInfo.processingProgress = null; // Clear progress when done - fileInfo.processingResult = { + // Update session file with processing results + sessionFile.status = upsertResult.success ? 'processed' : 'failed'; + sessionFile.processingProgress = null; // Clear progress when done + sessionFile.processingResult = { totalChunks: upsertResult.totalChunks, successfulChunks: upsertResult.successfulChunks, failedChunks: upsertResult.failedChunks, processedAt: new Date().toISOString(), - startedAt: fileInfo.processingResult.startedAt, - duration: Date.now() - new Date(fileInfo.processingResult.startedAt).getTime(), + startedAt: sessionFile.processingResult.startedAt, + duration: Date.now() - new Date(sessionFile.processingResult.startedAt).getTime(), method: 'formdata' }; if (upsertResult.errors.length > 0) { - fileInfo.processingErrors = upsertResult.errors; - console.log(`Processing errors for ${fileInfo.originalName}:`, upsertResult.errors); + sessionFile.processingErrors = upsertResult.errors; + console.log(`Processing errors for ${sessionFile.originalName}:`, upsertResult.errors); } - console.log(`Document processing completed for: ${fileInfo.originalName}`); + console.log(`Document processing completed for: ${sessionFile.originalName}`); console.log(`Result: FormData upload ${upsertResult.success ? 'successful' : 'failed'}`); + console.log(`Updated session file status to: ${sessionFile.status}`); + + // Verify the session file was updated + console.log('Session file after update:', { id: sessionFile.id, name: sessionFile.originalName, status: sessionFile.status }); + console.log('All session files after update:', session.uploadedFiles.map(f => ({ id: f.id, name: f.originalName, status: f.status }))); + + // Force session save since we modified it in an async context + session.save((err) => { + if (err) { + console.error('Error saving session after document processing:', err); + } else { + console.log('Session saved successfully after document processing'); + } + }); // Log final upsert verification console.log('Final FormData upsert verification:', { documentStore: `${FLOWISE_BASE_URL}/api/v1/document-store/${FLOWISE_DOCUMENT_STORE_ID}`, - documentId: fileInfo.id, - fileName: fileInfo.originalName, - success: upsertResult.success + documentId: sessionFile.id, + fileName: sessionFile.originalName, + success: upsertResult.success, + finalStatus: sessionFile.status }); } catch (error) { console.error(`Error processing document ${fileInfo.originalName}:`, error); - fileInfo.status = 'failed'; - fileInfo.processingError = error.message; - fileInfo.processingResult = fileInfo.processingResult || {}; - fileInfo.processingResult.failedAt = new Date().toISOString(); + + // Find the file in the session to update it with error + const sessionFile = session.uploadedFiles.find(f => f.id === fileInfo.id); + if (sessionFile) { + sessionFile.status = 'failed'; + sessionFile.processingError = error.message; + sessionFile.processingResult = sessionFile.processingResult || {}; + sessionFile.processingResult.failedAt = new Date().toISOString(); + console.log(`Updated session file status to: ${sessionFile.status} due to error`); + + // Force session save since we modified it in an async context + session.save((err) => { + if (err) { + console.error('Error saving session after processing error:', err); + } else { + console.log('Session saved successfully after processing error'); + } + }); + } } } @@ -806,34 +847,115 @@ app.post('/api/revise', requireAuth, async (req, res) => { }); app.get('/chat', requireAuth, (req, res) => { + // Initialize chat history if it doesn't exist + if (!req.session.chatHistory) { + req.session.chatHistory = []; + } + res.render('chat', { - title: 'Chat with EduCat AI' + title: 'Chat with EduCat AI', + chatHistory: req.session.chatHistory }); }); app.post('/api/chat', requireAuth, async (req, res) => { try { - const { message, history } = req.body; + const { message } = req.body; - // Call Flowise API for chat + // Initialize chat history in session if it doesn't exist + if (!req.session.chatHistory) { + req.session.chatHistory = []; + } + + console.log('Chat message received:', message); + console.log('Current chat history length:', req.session.chatHistory.length); + + // Call Flowise API for chat with session history const response = await axios.post(`${FLOWISE_API_URL}/${FLOWISE_CHATFLOW_ID}`, { question: message, - history: history || [] + history: req.session.chatHistory }); + const aiResponse = response.data.text || response.data.answer || 'No response received'; + + // Add the conversation to session history + req.session.chatHistory.push({ + human: message, + ai: aiResponse + }); + + // Save session explicitly since we modified it + req.session.save((err) => { + if (err) { + console.error('Error saving chat session:', err); + } + }); + + console.log('Updated chat history length:', req.session.chatHistory.length); + res.json({ success: true, - response: response.data.text || response.data.answer || 'No response received' + response: aiResponse }); } catch (error) { console.error('Chat error:', error); res.status(500).json({ + success: false, error: 'Failed to get chat response', details: error.message }); } }); +// Get chat history from session +app.get('/api/chat/history', requireAuth, (req, res) => { + try { + const chatHistory = req.session.chatHistory || []; + console.log('Chat history requested, returning', chatHistory.length, 'messages'); + + res.json({ + success: true, + history: chatHistory + }); + } catch (error) { + console.error('Error getting chat history:', error); + res.status(500).json({ + success: false, + error: 'Failed to get chat history', + details: error.message + }); + } +}); + +// Clear chat history +app.delete('/api/chat/history', requireAuth, (req, res) => { + try { + req.session.chatHistory = []; + req.session.save((err) => { + if (err) { + console.error('Error clearing chat session:', err); + return res.status(500).json({ + success: false, + error: 'Failed to clear chat history' + }); + } + + console.log('Chat history cleared for user:', req.session.userId); + res.json({ + success: true, + message: 'Chat history cleared' + }); + }); + } catch (error) { + console.error('Error clearing chat history:', error); + res.status(500).json({ + success: false, + error: 'Failed to clear chat history', + details: error.message + }); + } +}); + app.get('/dashboard', requireAuth, (req, res) => { const files = req.session.uploadedFiles || []; res.render('dashboard', { @@ -921,6 +1043,12 @@ app.get('/api/files/:fileId/status', requireAuth, async (req, res) => { const files = req.session.uploadedFiles || []; const file = files.find(f => f.id === fileId); + console.log(`Status requested for file ${fileId}`); + console.log(`Session ID: ${req.session.id || req.sessionID}`); + console.log(`User ID: ${req.session.userId}`); + console.log(`Files in session:`, files.map(f => ({ id: f.id, name: f.originalName, status: f.status }))); + console.log(`Found file:`, file ? { id: file.id, name: file.originalName, status: file.status } : 'Not found'); + if (!file) { return res.status(404).json({ success: false, error: 'File not found' }); } @@ -969,7 +1097,7 @@ app.post('/api/files/:fileId/retry', requireAuth, async (req, res) => { file.processingErrors = null; // Start processing asynchronously - processDocumentAsync(file, req.session.userId); + processDocumentAsync(file, req.session.userId, req.session); res.json({ success: true, @@ -989,6 +1117,8 @@ app.post('/api/files/:fileId/retry', requireAuth, async (req, res) => { app.get('/api/files/status/all', requireAuth, async (req, res) => { try { const files = req.session.uploadedFiles || []; + console.log('Status check requested for session files:', files.map(f => ({ id: f.id, name: f.originalName, status: f.status }))); + const statusSummary = files.map(file => ({ id: file.id, originalName: file.originalName, @@ -1010,6 +1140,8 @@ app.get('/api/files/status/all', requireAuth, async (req, res) => { files: statusSummary }; + console.log('Returning status summary:', summary); + res.json({ success: true, summary: summary diff --git a/views/chat.ejs b/views/chat.ejs index dfbc85c..156b7c1 100644 --- a/views/chat.ejs +++ b/views/chat.ejs @@ -5,23 +5,16 @@
-

Chat with EduCat AI

+
+

Chat with EduCat AI

+ +
-
-
-
- -
-
-
-

Hello! I'm EduCat AI, your study assistant. I can help you with questions about your notes, study techniques, and academic topics. How can I assist you today?

-
- Just now -
-
-
+
@@ -59,12 +52,17 @@
diff --git a/views/dashboard.ejs b/views/dashboard.ejs index fe8eea1..c47d1ed 100644 --- a/views/dashboard.ejs +++ b/views/dashboard.ejs @@ -5,9 +5,14 @@

Your Dashboard

- - Upload New Notes - +
+ + + Upload New Notes + +
<% if (files.length === 0) { %> @@ -497,6 +502,49 @@ async function viewProcessingDetails(fileId) { // Progress monitoring for processing files let progressMonitoring = {}; +// Manual refresh function +async function refreshStatus() { + const refreshBtn = document.getElementById('refreshBtn'); + const originalHtml = refreshBtn.innerHTML; + + refreshBtn.innerHTML = 'Refreshing...'; + refreshBtn.disabled = true; + + try { + const response = await fetch('/api/files/status/all'); + const result = await response.json(); + + if (result.success) { + console.log('Status refresh successful:', result.summary); + + // Check if status has changed - if so, reload page to show updates + const hasProcessingFiles = result.summary.processing > 0; + const currentProcessingBadges = document.querySelectorAll('.badge').length; + + if (!hasProcessingFiles || result.summary.processed > 0) { + console.log('Status changed, reloading page...'); + location.reload(); + } else { + // Show success feedback + refreshBtn.innerHTML = 'Updated'; + setTimeout(() => { + refreshBtn.innerHTML = originalHtml; + refreshBtn.disabled = false; + }, 1500); + } + } else { + throw new Error(result.error || 'Failed to refresh status'); + } + } catch (error) { + console.error('Error refreshing status:', error); + refreshBtn.innerHTML = 'Error'; + setTimeout(() => { + refreshBtn.innerHTML = originalHtml; + refreshBtn.disabled = false; + }, 2000); + } +} + function startProgressMonitoring() { // Find all processing files and start monitoring them const processingCards = document.querySelectorAll('.card'); @@ -534,19 +582,19 @@ function startFileProgressMonitoring(fileId) { // Stop monitoring if processing is complete if (result.progress.status !== 'processing') { + console.log(`File ${fileId} finished processing with status: ${result.progress.status}`); clearInterval(progressMonitoring[fileId]); delete progressMonitoring[fileId]; - // Refresh page to show final status - setTimeout(() => { - location.reload(); - }, 2000); + // Reload page immediately to show final status + console.log('Reloading page to show updated status...'); + location.reload(); } } } catch (error) { console.error('Error checking progress:', error); } - }, 2000); // Check every 2 seconds + }, 1000); // Check every 1 second for faster updates } function updateProgressDisplay(fileId, progress) { @@ -631,7 +679,7 @@ function stopAllProgressMonitoring() { // Clean up when page unloads window.addEventListener('beforeunload', stopAllProgressMonitoring); -// Auto-refresh processing status every 10 seconds for files that are still processing +// Auto-refresh processing status for files that are still processing document.addEventListener('DOMContentLoaded', function() { // Start progress monitoring for processing files startProgressMonitoring(); @@ -648,8 +696,31 @@ document.addEventListener('DOMContentLoaded', function() { if (hasProcessingFiles) { console.log('Found processing files, setting up auto-refresh...'); - setInterval(() => { - // Check again if there are still processing files + + // More frequent checking - every 3 seconds + const statusCheckInterval = setInterval(async () => { + console.log('Checking file statuses...'); + + try { + const response = await fetch('/api/files/status/all'); + const result = await response.json(); + + if (result.success) { + console.log('Status check result:', result.summary); + + // If no more processing files, reload page to show final status + if (result.summary.processing === 0) { + console.log('No more processing files, reloading page...'); + clearInterval(statusCheckInterval); + location.reload(); + return; + } + } + } catch (error) { + console.error('Error in status check:', error); + } + + // Also check DOM for processing badges const currentBadges = document.querySelectorAll('.badge'); let stillProcessing = false; @@ -659,11 +730,22 @@ document.addEventListener('DOMContentLoaded', function() { } }); - if (stillProcessing) { - console.log('Still have processing files, refreshing page...'); + if (!stillProcessing) { + console.log('No processing badges found, reloading page...'); + clearInterval(statusCheckInterval); location.reload(); } - }, 10000); // Check every 10 seconds + }, 3000); // Check every 3 seconds + + // Also add a manual refresh reminder + setTimeout(() => { + const processingBadges = Array.from(document.querySelectorAll('.badge')).filter(badge => + badge.textContent && badge.textContent.includes('Processing') + ); + if (processingBadges.length > 0) { + console.log('Files still processing after 10 seconds, you can click refresh manually'); + } + }, 10000); } });