Pre-cleanup commit: Current state before service layer consolidation
This commit is contained in:
@@ -10,7 +10,7 @@ import Analytics from './components/Analytics';
|
||||
import UploadMonitoringDashboard from './components/UploadMonitoringDashboard';
|
||||
import LogoutButton from './components/LogoutButton';
|
||||
import { documentService, GCSErrorHandler, GCSError } from './services/documentService';
|
||||
import { debugAuth, testAPIAuth } from './utils/authDebug';
|
||||
// import { debugAuth, testAPIAuth } from './utils/authDebug';
|
||||
|
||||
import {
|
||||
Home,
|
||||
@@ -75,13 +75,14 @@ const Dashboard: React.FC = () => {
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
// The API returns an array directly, not wrapped in success/data
|
||||
if (Array.isArray(result)) {
|
||||
// The API returns documents wrapped in a documents property
|
||||
const documentsArray = result.documents || result;
|
||||
if (Array.isArray(documentsArray)) {
|
||||
// Transform backend data to frontend format
|
||||
const transformedDocs = result.map((doc: any) => ({
|
||||
const transformedDocs = documentsArray.map((doc: any) => ({
|
||||
id: doc.id,
|
||||
name: doc.name || doc.originalName,
|
||||
originalName: doc.originalName,
|
||||
name: doc.name || doc.originalName || 'Unknown',
|
||||
originalName: doc.originalName || doc.name || 'Unknown',
|
||||
status: mapBackendStatus(doc.status),
|
||||
uploadedAt: doc.uploadedAt,
|
||||
processedAt: doc.processedAt,
|
||||
@@ -216,10 +217,22 @@ const Dashboard: React.FC = () => {
|
||||
return () => clearInterval(refreshInterval);
|
||||
}, [fetchDocuments]);
|
||||
|
||||
const handleUploadComplete = (fileId: string) => {
|
||||
console.log('Upload completed:', fileId);
|
||||
// Refresh documents list after upload
|
||||
fetchDocuments();
|
||||
const handleUploadComplete = (documentId: string) => {
|
||||
console.log('Upload completed:', documentId);
|
||||
// Add the new document to the list with a "processing" status
|
||||
// Since we only have the ID, we'll create a minimal document object
|
||||
const newDocument = {
|
||||
id: documentId,
|
||||
status: 'processing',
|
||||
name: 'Processing...',
|
||||
originalName: 'Processing...',
|
||||
uploadedAt: new Date().toISOString(),
|
||||
fileSize: 0,
|
||||
user_id: user?.id || '',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
setDocuments(prev => [...prev, newDocument]);
|
||||
};
|
||||
|
||||
const handleUploadError = (error: string) => {
|
||||
@@ -291,18 +304,18 @@ const Dashboard: React.FC = () => {
|
||||
setViewingDocument(null);
|
||||
};
|
||||
|
||||
// Debug functions
|
||||
const handleDebugAuth = async () => {
|
||||
await debugAuth();
|
||||
};
|
||||
// Debug functions (commented out for now)
|
||||
// const handleDebugAuth = async () => {
|
||||
// await debugAuth();
|
||||
// };
|
||||
|
||||
const handleTestAPIAuth = async () => {
|
||||
await testAPIAuth();
|
||||
};
|
||||
// const handleTestAPIAuth = async () => {
|
||||
// await testAPIAuth();
|
||||
// };
|
||||
|
||||
const filteredDocuments = documents.filter(doc =>
|
||||
doc.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
doc.originalName.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
(doc.name?.toLowerCase() || '').includes(searchTerm.toLowerCase()) ||
|
||||
(doc.originalName?.toLowerCase() || '').includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const stats = {
|
||||
|
||||
@@ -21,7 +21,7 @@ interface UploadedFile {
|
||||
}
|
||||
|
||||
interface DocumentUploadProps {
|
||||
onUploadComplete?: (fileId: string) => void;
|
||||
onUploadComplete?: (documentId: string) => void;
|
||||
onUploadError?: (error: string) => void;
|
||||
}
|
||||
|
||||
@@ -104,15 +104,15 @@ const DocumentUpload: React.FC<DocumentUploadProps> = ({
|
||||
abortController.signal
|
||||
);
|
||||
|
||||
// Upload completed - update status to "uploaded"
|
||||
// Upload completed - update status to "processing" immediately
|
||||
setUploadedFiles(prev =>
|
||||
prev.map(f =>
|
||||
f.id === uploadedFile.id
|
||||
? {
|
||||
...f,
|
||||
id: document.id,
|
||||
documentId: document.id,
|
||||
status: 'uploaded',
|
||||
id: result.id,
|
||||
documentId: result.id,
|
||||
status: 'processing', // Changed from 'uploaded' to 'processing'
|
||||
progress: 100
|
||||
}
|
||||
: f
|
||||
@@ -120,10 +120,10 @@ const DocumentUpload: React.FC<DocumentUploadProps> = ({
|
||||
);
|
||||
|
||||
// Call the completion callback with the document ID
|
||||
onUploadComplete?.(document.id);
|
||||
onUploadComplete?.(result.id);
|
||||
|
||||
// Start monitoring processing progress
|
||||
monitorProcessingProgress(document.id, uploadedFile.id);
|
||||
// Start monitoring processing progress immediately
|
||||
monitorProcessingProgress(result.id, uploadedFile.id);
|
||||
|
||||
} catch (error) {
|
||||
// Check if this was an abort error
|
||||
@@ -189,8 +189,29 @@ const DocumentUpload: React.FC<DocumentUploadProps> = ({
|
||||
console.warn('Attempted to monitor progress for document with invalid UUID format:', documentId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add timeout to prevent infinite polling (30 minutes max)
|
||||
const startTime = Date.now();
|
||||
const maxPollingTime = 30 * 60 * 1000; // 30 minutes
|
||||
|
||||
const checkProgress = async () => {
|
||||
// Check if we've exceeded the maximum polling time
|
||||
if (Date.now() - startTime > maxPollingTime) {
|
||||
console.warn(`Polling timeout for document ${documentId} after ${maxPollingTime / 1000 / 60} minutes`);
|
||||
setUploadedFiles(prev =>
|
||||
prev.map(f =>
|
||||
f.id === fileId
|
||||
? {
|
||||
...f,
|
||||
status: 'error',
|
||||
error: 'Processing timeout - please check document status manually'
|
||||
}
|
||||
: f
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${import.meta.env.VITE_API_BASE_URL}/documents/${documentId}/progress`, {
|
||||
headers: {
|
||||
@@ -203,8 +224,10 @@ const DocumentUpload: React.FC<DocumentUploadProps> = ({
|
||||
const progress = await response.json();
|
||||
|
||||
// Update status based on progress
|
||||
let newStatus: UploadedFile['status'] = 'uploaded';
|
||||
if (progress.status === 'processing' || progress.status === 'extracting_text' || progress.status === 'processing_llm' || progress.status === 'generating_pdf') {
|
||||
let newStatus: UploadedFile['status'] = 'processing'; // Default to processing
|
||||
if (progress.status === 'uploading' || progress.status === 'uploaded') {
|
||||
newStatus = 'processing'; // Still processing
|
||||
} else if (progress.status === 'processing' || progress.status === 'extracting_text' || progress.status === 'processing_llm' || progress.status === 'generating_pdf') {
|
||||
newStatus = 'processing';
|
||||
} else if (progress.status === 'completed') {
|
||||
newStatus = 'completed';
|
||||
@@ -242,12 +265,12 @@ const DocumentUpload: React.FC<DocumentUploadProps> = ({
|
||||
// Don't stop monitoring on network errors, just log and continue
|
||||
}
|
||||
|
||||
// Continue monitoring
|
||||
setTimeout(checkProgress, 2000);
|
||||
// Continue monitoring with shorter intervals for better responsiveness
|
||||
setTimeout(checkProgress, 3000); // Check every 3 seconds
|
||||
};
|
||||
|
||||
// Start monitoring
|
||||
setTimeout(checkProgress, 1000);
|
||||
// Start monitoring immediately
|
||||
setTimeout(checkProgress, 500); // Start checking after 500ms
|
||||
}, [token]);
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||
@@ -378,7 +401,7 @@ const DocumentUpload: React.FC<DocumentUploadProps> = ({
|
||||
<h4 className="text-sm font-medium text-success-800">Upload Complete</h4>
|
||||
<p className="text-sm text-success-700 mt-1">
|
||||
Files have been uploaded successfully to Firebase Storage! You can now navigate away from this page.
|
||||
Processing will continue in the background using Document AI + Optimized Agentic RAG. PDFs will be automatically deleted after processing to save costs.
|
||||
Processing will continue in the background using Document AI + Optimized Agentic RAG. This can take several minutes. PDFs will be automatically deleted after processing to save costs.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ const API_BASE_URL = config.apiBaseUrl;
|
||||
// Create axios instance with auth interceptor
|
||||
const apiClient = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
timeout: 30000, // 30 seconds
|
||||
timeout: 300000, // 5 minutes
|
||||
});
|
||||
|
||||
// Add auth token to requests
|
||||
@@ -263,14 +263,46 @@ class DocumentService {
|
||||
// Step 3: Confirm upload and trigger processing
|
||||
onProgress?.(95); // 95% - Confirming upload
|
||||
|
||||
const confirmResponse = await apiClient.post(`/documents/${documentId}/confirm-upload`, {}, { signal });
|
||||
console.log('🔄 Making confirm-upload request for document:', documentId);
|
||||
console.log('🔄 Confirm-upload URL:', `/documents/${documentId}/confirm-upload`);
|
||||
|
||||
// Add retry logic for confirm-upload (based on Google Cloud best practices)
|
||||
let confirmResponse;
|
||||
let lastError;
|
||||
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
console.log(`🔄 Confirm-upload attempt ${attempt}/3`);
|
||||
confirmResponse = await apiClient.post(`/documents/${documentId}/confirm-upload`, {}, {
|
||||
signal,
|
||||
timeout: 60000 // 60 second timeout for confirm-upload
|
||||
});
|
||||
console.log('✅ Confirm-upload response received:', confirmResponse.status);
|
||||
console.log('✅ Confirm-upload response data:', confirmResponse.data);
|
||||
break; // Success, exit retry loop
|
||||
} catch (error: any) {
|
||||
lastError = error;
|
||||
console.log(`❌ Confirm-upload attempt ${attempt} failed:`, error.message);
|
||||
|
||||
if (attempt < 3) {
|
||||
// Wait before retry (exponential backoff)
|
||||
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s
|
||||
console.log(`⏳ Waiting ${delay}ms before retry...`);
|
||||
await new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!confirmResponse) {
|
||||
throw lastError || new Error('Confirm-upload failed after 3 attempts');
|
||||
}
|
||||
|
||||
onProgress?.(100); // 100% - Complete
|
||||
console.log('✅ Upload confirmed and processing started');
|
||||
|
||||
return {
|
||||
id: documentId,
|
||||
...confirmResponse.data
|
||||
...confirmResponse.data.document
|
||||
};
|
||||
|
||||
} catch (error: any) {
|
||||
@@ -281,6 +313,16 @@ class DocumentService {
|
||||
throw new Error('Upload was cancelled.');
|
||||
}
|
||||
|
||||
// Handle network timeouts
|
||||
if (error.code === 'ECONNABORTED' || error.message?.includes('timeout')) {
|
||||
throw new Error('Request timed out. Please check your connection and try again.');
|
||||
}
|
||||
|
||||
// Handle network errors
|
||||
if (error.code === 'ERR_NETWORK' || error.message?.includes('Network Error')) {
|
||||
throw new Error('Network error. Please check your connection and try again.');
|
||||
}
|
||||
|
||||
if (error.response?.status === 401) {
|
||||
throw new Error('Authentication required. Please log in again.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user