port-nimara-client-portal/server/api/eoi/upload-document.ts

177 lines
5.8 KiB
TypeScript
Raw Normal View History

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);
if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) {
2025-06-10 15:01:04 +02:00
console.error('[EOI Upload] Authentication failed - invalid x-tag');
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);
if (!interestId) {
2025-06-10 15:01:04 +02:00
console.error('[EOI Upload] No interest ID provided');
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');
// 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');
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');
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
});
// Read file buffer
2025-06-10 15:01:04 +02:00
console.log('[EOI Upload] Reading file from disk');
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
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);
// 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 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}`);
}
// Clean up temp file
2025-06-10 15:01:04 +02:00
console.log('[EOI Upload] Cleaning up temp file');
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
// 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');
await updateInterestEOIDocument(interestId, documentData);
// Also update the status fields
const updateData: any = {
'EOI Status': 'Waiting for Signatures',
'EOI Time Sent': new Date().toISOString()
};
2025-06-10 15:01:04 +02:00
console.log('[EOI Upload] Updating interest status fields');
// Update Sales Process Level if it's below "LOI and NDA Sent"
const currentLevel = await getCurrentSalesLevel(interestId);
if (shouldUpdateSalesLevel(currentLevel)) {
updateData['Sales Process Level'] = 'LOI and NDA Sent';
}
// Update the interest
await $fetch('/api/update-interest', {
method: 'POST',
headers: {
'x-tag': xTagHeader,
},
body: {
id: interestId,
data: updateData
}
});
2025-06-10 15:01:04 +02:00
console.log('[EOI Upload] Upload completed successfully');
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);
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);
}