2025-06-10 13:59:09 +02:00
|
|
|
import { uploadFile, createBucketIfNotExists, getMinioClient } from '~/server/utils/minio';
|
|
|
|
|
import { updateInterestEOIDocument } from '~/server/utils/nocodb';
|
|
|
|
|
import formidable from 'formidable';
|
|
|
|
|
import { promises as fs } from 'fs';
|
|
|
|
|
import mime from 'mime-types';
|
|
|
|
|
|
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
|
|
|
const xTagHeader = getRequestHeader(event, "x-tag");
|
|
|
|
|
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Request received with x-tag:', xTagHeader);
|
|
|
|
|
|
2025-06-10 13:59:09 +02:00
|
|
|
if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) {
|
2025-06-10 15:01:04 +02:00
|
|
|
console.error('[EOI Upload] Authentication failed - invalid x-tag');
|
2025-06-10 13:59:09 +02:00
|
|
|
throw createError({ statusCode: 401, statusMessage: "unauthenticated" });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Get interestId from query params
|
|
|
|
|
const query = getQuery(event);
|
|
|
|
|
const interestId = query.interestId as string;
|
|
|
|
|
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Interest ID:', interestId);
|
|
|
|
|
|
2025-06-10 13:59:09 +02:00
|
|
|
if (!interestId) {
|
2025-06-10 15:01:04 +02:00
|
|
|
console.error('[EOI Upload] No interest ID provided');
|
2025-06-10 13:59:09 +02:00
|
|
|
throw createError({
|
|
|
|
|
statusCode: 400,
|
|
|
|
|
statusMessage: 'Interest ID is required',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 14:52:39 +02:00
|
|
|
// Ensure bucket exists
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Ensuring client-portal bucket exists');
|
2025-06-10 14:52:39 +02:00
|
|
|
await createBucketIfNotExists('client-portal');
|
2025-06-10 13:59:09 +02:00
|
|
|
|
|
|
|
|
// Parse multipart form data
|
|
|
|
|
const form = formidable({
|
|
|
|
|
maxFileSize: 50 * 1024 * 1024, // 50MB limit
|
|
|
|
|
keepExtensions: true,
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Parsing multipart form data');
|
2025-06-10 13:59:09 +02:00
|
|
|
const [fields, files] = await form.parse(event.node.req);
|
|
|
|
|
|
|
|
|
|
// Handle the uploaded file
|
|
|
|
|
const uploadedFile = Array.isArray(files.file) ? files.file[0] : files.file;
|
|
|
|
|
|
|
|
|
|
if (!uploadedFile) {
|
2025-06-10 15:01:04 +02:00
|
|
|
console.error('[EOI Upload] No file uploaded in request');
|
2025-06-10 13:59:09 +02:00
|
|
|
throw createError({
|
|
|
|
|
statusCode: 400,
|
|
|
|
|
statusMessage: 'No file uploaded',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] File received:', {
|
|
|
|
|
originalFilename: uploadedFile.originalFilename,
|
|
|
|
|
size: uploadedFile.size,
|
|
|
|
|
mimetype: uploadedFile.mimetype
|
|
|
|
|
});
|
|
|
|
|
|
2025-06-10 13:59:09 +02:00
|
|
|
// Read file buffer
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Reading file from disk');
|
2025-06-10 13:59:09 +02:00
|
|
|
const fileBuffer = await fs.readFile(uploadedFile.filepath);
|
|
|
|
|
|
2025-06-10 15:01:04 +02:00
|
|
|
// Generate filename with timestamp - ensure it goes to EOIs folder
|
2025-06-10 13:59:09 +02:00
|
|
|
const timestamp = Date.now();
|
|
|
|
|
const sanitizedName = uploadedFile.originalFilename?.replace(/[^a-zA-Z0-9.-]/g, '_') || 'eoi-document.pdf';
|
|
|
|
|
const fileName = `EOIs/${interestId}-${timestamp}-${sanitizedName}`;
|
|
|
|
|
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Generated filename:', fileName);
|
|
|
|
|
|
2025-06-10 13:59:09 +02:00
|
|
|
// Get content type
|
|
|
|
|
const contentType = mime.lookup(uploadedFile.originalFilename || '') || 'application/pdf';
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Content type:', contentType);
|
2025-06-10 13:59:09 +02:00
|
|
|
|
2025-06-10 14:52:39 +02:00
|
|
|
// Upload to MinIO client-portal bucket
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Uploading to MinIO client-portal bucket');
|
2025-06-10 14:52:39 +02:00
|
|
|
const client = getMinioClient();
|
2025-06-10 15:01:04 +02:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await client.putObject('client-portal', fileName, fileBuffer, fileBuffer.length, {
|
|
|
|
|
'Content-Type': contentType,
|
|
|
|
|
});
|
|
|
|
|
console.log('[EOI Upload] Successfully uploaded to MinIO');
|
|
|
|
|
} catch (minioError: any) {
|
|
|
|
|
console.error('[EOI Upload] MinIO upload failed:', minioError);
|
|
|
|
|
throw new Error(`Failed to upload to MinIO: ${minioError.message}`);
|
|
|
|
|
}
|
2025-06-10 13:59:09 +02:00
|
|
|
|
|
|
|
|
// Clean up temp file
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Cleaning up temp file');
|
2025-06-10 13:59:09 +02:00
|
|
|
await fs.unlink(uploadedFile.filepath);
|
|
|
|
|
|
|
|
|
|
// Get download URL for the uploaded file
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Generating presigned URL');
|
2025-06-10 14:52:39 +02:00
|
|
|
const url = await client.presignedGetObject('client-portal', fileName, 24 * 60 * 60); // 24 hour expiry
|
2025-06-10 13:59:09 +02:00
|
|
|
|
|
|
|
|
// Prepare document data for database
|
|
|
|
|
const documentData = {
|
|
|
|
|
title: uploadedFile.originalFilename || 'EOI Document',
|
|
|
|
|
filename: fileName,
|
|
|
|
|
url: url,
|
|
|
|
|
size: uploadedFile.size,
|
|
|
|
|
uploadedAt: new Date().toISOString()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Update interest with EOI document information
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Updating interest with EOI document info');
|
2025-06-10 16:48:40 +02:00
|
|
|
console.log('[EOI Upload] Document data:', JSON.stringify(documentData, null, 2));
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await updateInterestEOIDocument(interestId, documentData);
|
|
|
|
|
console.log('[EOI Upload] Successfully updated EOI document in database');
|
|
|
|
|
} catch (dbError: any) {
|
|
|
|
|
console.error('[EOI Upload] Failed to update database with EOI document:', dbError);
|
|
|
|
|
console.error('[EOI Upload] Database error details:', dbError.data || dbError.message);
|
|
|
|
|
throw new Error(`Failed to update database: ${dbError.message}`);
|
|
|
|
|
}
|
2025-06-10 13:59:09 +02:00
|
|
|
|
2025-06-10 15:52:30 +02:00
|
|
|
// Update the status fields for uploaded (signed) EOI
|
2025-06-10 13:59:09 +02:00
|
|
|
const updateData: any = {
|
2025-06-10 15:52:30 +02:00
|
|
|
'EOI Status': 'Signed',
|
|
|
|
|
'EOI Time Sent': new Date().toISOString(),
|
|
|
|
|
'Sales Process Level': 'Signed LOI and NDA'
|
2025-06-10 13:59:09 +02:00
|
|
|
};
|
2025-06-10 15:52:30 +02:00
|
|
|
console.log('[EOI Upload] Updating interest status fields for signed EOI');
|
2025-06-10 16:48:40 +02:00
|
|
|
console.log('[EOI Upload] Status update data:', JSON.stringify(updateData, null, 2));
|
2025-06-10 13:59:09 +02:00
|
|
|
|
2025-06-10 16:48:40 +02:00
|
|
|
try {
|
|
|
|
|
// Update the interest
|
|
|
|
|
await $fetch('/api/update-interest', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'x-tag': xTagHeader,
|
|
|
|
|
},
|
|
|
|
|
body: {
|
|
|
|
|
id: interestId,
|
|
|
|
|
data: updateData
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
console.log('[EOI Upload] Successfully updated interest status');
|
|
|
|
|
} catch (statusError: any) {
|
|
|
|
|
console.error('[EOI Upload] Failed to update interest status:', statusError);
|
|
|
|
|
console.error('[EOI Upload] Status error details:', statusError.data || statusError.message);
|
|
|
|
|
// Don't throw here - the file was uploaded successfully
|
|
|
|
|
}
|
2025-06-10 13:59:09 +02:00
|
|
|
|
2025-06-10 15:01:04 +02:00
|
|
|
console.log('[EOI Upload] Upload completed successfully');
|
2025-06-10 13:59:09 +02:00
|
|
|
return {
|
|
|
|
|
success: true,
|
|
|
|
|
document: documentData,
|
|
|
|
|
message: 'EOI document uploaded successfully',
|
|
|
|
|
};
|
|
|
|
|
} catch (error: any) {
|
2025-06-10 15:01:04 +02:00
|
|
|
console.error('[EOI Upload] Failed to upload EOI document:', error);
|
|
|
|
|
console.error('[EOI Upload] Error stack:', error.stack);
|
2025-06-10 13:59:09 +02:00
|
|
|
throw createError({
|
|
|
|
|
statusCode: 500,
|
|
|
|
|
statusMessage: error.message || 'Failed to upload EOI document',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
async function getCurrentSalesLevel(interestId: string): Promise<string> {
|
|
|
|
|
try {
|
|
|
|
|
const interest = await $fetch(`/api/get-interest-by-id`, {
|
|
|
|
|
headers: {
|
|
|
|
|
'x-tag': '094ut234',
|
|
|
|
|
},
|
|
|
|
|
params: {
|
|
|
|
|
id: interestId,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
return interest['Sales Process Level'] || '';
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to get current sales level:', error);
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shouldUpdateSalesLevel(currentLevel: string): boolean {
|
|
|
|
|
const levelsBeforeLOI = [
|
|
|
|
|
'General Qualified Interest',
|
|
|
|
|
'Specific Qualified Interest'
|
|
|
|
|
];
|
|
|
|
|
return levelsBeforeLOI.includes(currentLevel);
|
|
|
|
|
}
|