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', }); } });