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

245 lines
9.7 KiB
TypeScript

import { requireAuth } from '~/server/utils/auth';
import { getInterestById, updateInterest } from '~/server/utils/nocodb';
import { checkDocumentSignatureStatus } from '~/server/utils/documeso';
import type { InterestSalesProcessLevel, EOIStatus } from '~/utils/types';
export default defineEventHandler(async (event) => {
// Check authentication (x-tag header OR Keycloak session)
await requireAuth(event);
console.log('[Delete Generated EOI] Request received');
console.log('[Delete Generated EOI] Request headers:', getHeaders(event));
console.log('[Delete Generated EOI] Request method:', getMethod(event));
try {
const body = await readBody(event);
const { interestId } = body;
const query = getQuery(event);
console.log('[Delete Generated EOI] Interest ID:', interestId);
console.log('[Delete Generated EOI] Query params:', query);
if (!interestId) {
console.error('[Delete Generated EOI] No interest ID provided');
throw createError({
statusCode: 400,
statusMessage: 'Interest ID is required. Please provide a valid interest ID.',
});
}
// Get current interest data
const interest = await getInterestById(interestId);
if (!interest) {
throw createError({
statusCode: 404,
statusMessage: 'Interest not found',
});
}
const documensoID = interest['documensoID'];
console.log('[Delete Generated EOI] Documenso ID:', documensoID);
if (!documensoID) {
throw createError({
statusCode: 400,
statusMessage: 'No generated document found to delete',
});
}
// Check if document is fully signed - prevent deletion if it is
try {
const signatureStatus = await checkDocumentSignatureStatus(parseInt(documensoID));
console.log('[Delete Generated EOI] Signature status:', signatureStatus);
if (signatureStatus.allSigned) {
throw createError({
statusCode: 400,
statusMessage: 'Cannot delete a fully signed document. All parties have already signed.',
});
}
} catch (error: any) {
console.error('[Delete Generated EOI] Failed to check signature status:', error);
// If it's a 404 error, the document is already gone from Documenso, so we can proceed with cleanup
if (error.status === 404 || error.statusCode === 404 || error.message?.includes('404')) {
console.log('[Delete Generated EOI] Document not found in Documenso (404) - proceeding with database cleanup');
} else {
console.warn('[Delete Generated EOI] Proceeding with deletion despite signature status check failure');
}
}
// Delete document from Documenso
const documensoApiKey = process.env.NUXT_DOCUMENSO_API_KEY;
const documensoBaseUrl = process.env.NUXT_DOCUMENSO_BASE_URL;
if (!documensoApiKey || !documensoBaseUrl) {
throw createError({
statusCode: 500,
statusMessage: "Documenso configuration missing. Please check NUXT_DOCUMENSO_API_KEY and NUXT_DOCUMENSO_BASE_URL environment variables."
});
}
console.log('[Delete Generated EOI] Deleting document from Documenso');
let documensoDeleteSuccessful = false;
let retryCount = 0;
const maxRetries = 3;
// Retry logic for temporary failures
while (!documensoDeleteSuccessful && retryCount < maxRetries) {
try {
const deleteResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${documensoID}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${documensoApiKey}`,
'Content-Type': 'application/json'
}
});
const responseStatus = deleteResponse.status;
let errorDetails = '';
try {
errorDetails = await deleteResponse.text();
} catch {
errorDetails = 'No error details available';
}
if (!deleteResponse.ok) {
console.error(`[Delete Generated EOI] Documenso deletion failed (attempt ${retryCount + 1}/${maxRetries}):`, {
status: responseStatus,
statusText: deleteResponse.statusText,
details: errorDetails
});
// Handle specific status codes
switch (responseStatus) {
case 404:
// Document already deleted - this is fine
console.log('[Delete Generated EOI] Document already deleted from Documenso (404) - proceeding with database cleanup');
documensoDeleteSuccessful = true;
break;
case 403:
// Permission denied - document might be in a protected state
console.warn('[Delete Generated EOI] Permission denied (403) - document may be in a protected state');
throw createError({
statusCode: 403,
statusMessage: 'Cannot delete document - it may be fully signed or in a protected state',
});
case 500:
case 502:
case 503:
case 504:
// Server errors - retry if we haven't exceeded retries
if (retryCount < maxRetries - 1) {
console.log(`[Delete Generated EOI] Server error (${responseStatus}) - retrying in ${(retryCount + 1) * 2} seconds...`);
await new Promise(resolve => setTimeout(resolve, (retryCount + 1) * 2000)); // Exponential backoff
retryCount++;
continue;
} else {
console.error('[Delete Generated EOI] Max retries exceeded for server error');
// Allow proceeding with cleanup for server errors after retries
if (query.forceCleanup === 'true') {
console.warn('[Delete Generated EOI] Force cleanup enabled - proceeding despite Documenso error');
documensoDeleteSuccessful = true;
break;
}
throw new Error(`Documenso server error after ${maxRetries} attempts (${responseStatus}): ${errorDetails}`);
}
default:
// Other errors - don't retry
throw new Error(`Documenso API error (${responseStatus}): ${errorDetails || deleteResponse.statusText}`);
}
} else {
console.log('[Delete Generated EOI] Successfully deleted document from Documenso');
documensoDeleteSuccessful = true;
}
} catch (error: any) {
console.error(`[Delete Generated EOI] Documenso deletion error (attempt ${retryCount + 1}/${maxRetries}):`, error);
// Network errors - retry if we haven't exceeded retries
if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
if (retryCount < maxRetries - 1) {
console.log(`[Delete Generated EOI] Network error - retrying in ${(retryCount + 1) * 2} seconds...`);
await new Promise(resolve => setTimeout(resolve, (retryCount + 1) * 2000));
retryCount++;
continue;
}
}
// Check if it's a 404 error wrapped in another error
if (error.message?.includes('404') || error.status === 404 || error.statusCode === 404) {
console.log('[Delete Generated EOI] Document not found in Documenso - proceeding with database cleanup');
documensoDeleteSuccessful = true;
break;
}
// Check if force cleanup is enabled
const query = getQuery(event);
if (query.forceCleanup === 'true') {
console.warn('[Delete Generated EOI] Force cleanup enabled - proceeding despite Documenso error:', error.message);
documensoDeleteSuccessful = true;
break;
}
// Don't wrap error messages multiple times
if (error.statusCode) {
throw error;
}
throw createError({
statusCode: 500,
statusMessage: error.message || 'Failed to communicate with Documenso API',
});
}
}
if (!documensoDeleteSuccessful) {
const query = getQuery(event);
if (query.forceCleanup === 'true') {
console.warn('[Delete Generated EOI] Force cleanup enabled - proceeding with database cleanup despite Documenso failure');
documensoDeleteSuccessful = true;
} else {
throw createError({
statusCode: 500,
statusMessage: 'Failed to delete document from Documenso after multiple attempts. You can add ?forceCleanup=true to force database cleanup.',
});
}
}
// Reset interest fields
const updateData = {
'EOI Status': 'Awaiting Further Details' as EOIStatus,
'Sales Process Level': 'Specific Qualified Interest' as InterestSalesProcessLevel,
'EOI Time Sent': undefined,
'Signature Link Client': undefined,
'Signature Link CC': undefined,
'Signature Link Developer': undefined,
'EmbeddedSignatureLinkClient': undefined,
'EmbeddedSignatureLinkCC': undefined,
'EmbeddedSignatureLinkDeveloper': undefined,
'documensoID': undefined
};
console.log('[Delete Generated EOI] Resetting interest fields');
// Update the interest
await updateInterest(interestId, updateData);
console.log('[Delete Generated EOI] Delete completed successfully');
return {
success: true,
message: 'Generated EOI document deleted successfully',
};
} catch (error: any) {
console.error('[Delete Generated EOI] Failed to delete generated EOI document:', error);
console.error('[Delete Generated EOI] Error stack:', error.stack);
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.statusMessage || error.message || 'Failed to delete generated EOI document',
});
}
});