FEAT: Correct spelling of 'Documenso' in API utility functions and add connectivity test for Documenso API

This commit is contained in:
Matt 2025-06-17 15:17:19 +02:00
parent 8d378f5b53
commit 0b881a2588
6 changed files with 112 additions and 40 deletions

View File

@ -0,0 +1,57 @@
export default defineEventHandler(async (event) => {
console.log('[DEBUG] Testing Documenso connectivity...')
try {
// Test basic connectivity to Documenso
const documensoBaseUrl = process.env.NUXT_DOCUMENSO_BASE_URL;
const documensoApiKey = process.env.NUXT_DOCUMENSO_API_KEY;
if (!documensoBaseUrl || !documensoApiKey) {
return {
success: false,
message: 'Documenso configuration missing',
error: {
message: 'NUXT_DOCUMENSO_BASE_URL or NUXT_DOCUMENSO_API_KEY environment variables not set'
}
}
}
const testUrl = `${documensoBaseUrl}/api/v1/documents`
const response = await $fetch(testUrl, {
method: 'GET',
headers: {
'Authorization': `Bearer ${documensoApiKey}`,
'Content-Type': 'application/json'
},
params: {
perPage: 1 // Just get 1 document to test connectivity
},
retry: 0
}) as any
return {
success: true,
message: 'Documenso connectivity successful',
endpoint: testUrl,
response: {
total: response.total || 0,
documentsFound: response.documents ? response.documents.length : 0,
statusCode: 200
}
}
} catch (error: any) {
console.error('[DEBUG] Documenso connectivity test failed:', error)
return {
success: false,
message: 'Documenso connectivity failed',
error: {
message: error.message,
status: error.status,
statusCode: error.statusCode,
cause: error.cause?.message
}
}
}
})

View File

@ -1,4 +1,4 @@
import { getDocumesoDocumentByExternalId, checkDocumentSignatureStatus } from '~/server/utils/documeso'; import { getDocumensoDocumentByExternalId, checkDocumentSignatureStatus } from '~/server/utils/documeso';
import { getInterestById, updateInterest } from '~/server/utils/nocodb'; import { getInterestById, updateInterest } from '~/server/utils/nocodb';
import { requireAuth } from '~/server/utils/auth'; import { requireAuth } from '~/server/utils/auth';
import type { InterestSalesProcessLevel, EOIStatus } from '~/utils/types'; import type { InterestSalesProcessLevel, EOIStatus } from '~/utils/types';
@ -89,7 +89,7 @@ export default defineEventHandler(async (event) => {
// Otherwise, try to find by external ID (using interestId) - fallback method // Otherwise, try to find by external ID (using interestId) - fallback method
console.log('[check-signature-status] No documensoID stored, trying external ID fallback'); console.log('[check-signature-status] No documensoID stored, trying external ID fallback');
const externalId = `loi-${interestId}`; const externalId = `loi-${interestId}`;
const document = await getDocumesoDocumentByExternalId(externalId); const document = await getDocumensoDocumentByExternalId(externalId);
if (!document) { if (!document) {
console.log('[check-signature-status] No document found by external ID either'); console.log('[check-signature-status] No document found by external ID either');

View File

@ -55,8 +55,13 @@ export default defineEventHandler(async (event) => {
} }
} catch (error: any) { } catch (error: any) {
console.error('[Delete Generated EOI] Failed to check signature status:', error); console.error('[Delete Generated EOI] Failed to check signature status:', error);
// If we can't check status, we'll proceed with deletion but warn
console.warn('[Delete Generated EOI] Proceeding with deletion despite signature status check failure'); // 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 // Delete document from Documenso

View File

@ -1,5 +1,5 @@
import { requireAuth } from '~/server/utils/auth'; import { requireAuth } from '~/server/utils/auth';
import { getDocumesoDocument, checkDocumentSignatureStatus, formatRecipientName } from '~/server/utils/documeso'; import { getDocumensoDocument, checkDocumentSignatureStatus, formatRecipientName } from '~/server/utils/documeso';
import { getInterestById } from '~/server/utils/nocodb'; import { getInterestById } from '~/server/utils/nocodb';
import { sendEmail } from '~/server/utils/email'; import { sendEmail } from '~/server/utils/email';
@ -45,7 +45,7 @@ export default defineEventHandler(async (event) => {
} }
// Get document and check signature status // Get document and check signature status
const document = await getDocumesoDocument(parseInt(documentId)); const document = await getDocumensoDocument(parseInt(documentId));
const status = await checkDocumentSignatureStatus(parseInt(documentId)); const status = await checkDocumentSignatureStatus(parseInt(documentId));
const emailsToSend: ReminderEmail[] = []; const emailsToSend: ReminderEmail[] = [];

View File

@ -1,5 +1,5 @@
import { getInterestById, updateInterest } from '~/server/utils/nocodb'; import { getInterestById, updateInterest } from '~/server/utils/nocodb';
import { getDocumesoDocument } from '~/server/utils/documeso'; import { getDocumensoDocument } from '~/server/utils/documeso';
import { requireAuth } from '~/server/utils/auth'; import { requireAuth } from '~/server/utils/auth';
import type { InterestSalesProcessLevel, EOIStatus } from '~/utils/types'; import type { InterestSalesProcessLevel, EOIStatus } from '~/utils/types';
@ -52,7 +52,7 @@ export default defineEventHandler(async (event) => {
try { try {
console.log('[Validate Document] Checking document existence in Documenso'); console.log('[Validate Document] Checking document existence in Documenso');
const document = await getDocumesoDocument(parseInt(documensoID)); const document = await getDocumensoDocument(parseInt(documensoID));
documentStatus = document.status; documentStatus = document.status;
console.log('[Validate Document] Document exists with status:', documentStatus); console.log('[Validate Document] Document exists with status:', documentStatus);
} catch (error: any) { } catch (error: any) {

View File

@ -1,10 +1,10 @@
// Documeso API client utilities // Documenso API client utilities
interface DocumesoConfig { interface DocumensoConfig {
apiUrl: string; apiUrl: string;
apiKey: string; apiKey: string;
} }
interface DocumesoRecipient { interface DocumensoRecipient {
id: number; id: number;
documentId: number; documentId: number;
email: string; email: string;
@ -19,7 +19,7 @@ interface DocumesoRecipient {
signingUrl: string; signingUrl: string;
} }
interface DocumesoDocument { interface DocumensoDocument {
id: number; id: number;
externalId: string; externalId: string;
userId: number; userId: number;
@ -30,30 +30,40 @@ interface DocumesoDocument {
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
completedAt: string | null; completedAt: string | null;
recipients: DocumesoRecipient[]; recipients: DocumensoRecipient[];
} }
interface DocumesoListResponse { interface DocumensoListResponse {
documents: DocumesoDocument[]; documents: DocumensoDocument[];
total: number; total: number;
page: number; page: number;
perPage: number; perPage: number;
} }
// Get Documeso configuration // Get Documenso configuration from environment variables
const getDocumesoConfig = (): DocumesoConfig => { const getDocumensoConfig = (): DocumensoConfig => {
const apiUrl = process.env.NUXT_DOCUMENSO_BASE_URL;
const apiKey = process.env.NUXT_DOCUMENSO_API_KEY;
if (!apiUrl || !apiKey) {
throw createError({
statusCode: 500,
statusMessage: 'Documenso configuration missing. Please check NUXT_DOCUMENSO_BASE_URL and NUXT_DOCUMENSO_API_KEY environment variables.'
});
}
return { return {
apiUrl: 'https://signatures.portnimara.dev/api/v1', apiUrl: `${apiUrl}/api/v1`,
apiKey: 'Bearer api_malptg62zqyb0wrp' apiKey: `Bearer ${apiKey}`
}; };
}; };
// Fetch a single document by ID // Fetch a single document by ID
export const getDocumesoDocument = async (documentId: number): Promise<DocumesoDocument> => { export const getDocumensoDocument = async (documentId: number): Promise<DocumensoDocument> => {
const config = getDocumesoConfig(); const config = getDocumensoConfig();
try { try {
const response = await $fetch<DocumesoDocument>(`${config.apiUrl}/documents/${documentId}`, { const response = await $fetch<DocumensoDocument>(`${config.apiUrl}/documents/${documentId}`, {
headers: { headers: {
'Authorization': config.apiKey, 'Authorization': config.apiKey,
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -62,17 +72,17 @@ export const getDocumesoDocument = async (documentId: number): Promise<DocumesoD
return response; return response;
} catch (error) { } catch (error) {
console.error('Failed to fetch Documeso document:', error); console.error('Failed to fetch Documenso document:', error);
throw error; throw error;
} }
}; };
// Search documents by external ID (e.g., 'loi-94') // Search documents by external ID (e.g., 'loi-94')
export const searchDocumesoDocuments = async (externalId?: string): Promise<DocumesoDocument[]> => { export const searchDocumensoDocuments = async (externalId?: string): Promise<DocumensoDocument[]> => {
const config = getDocumesoConfig(); const config = getDocumensoConfig();
try { try {
const response = await $fetch<DocumesoListResponse>(`${config.apiUrl}/documents`, { const response = await $fetch<DocumensoListResponse>(`${config.apiUrl}/documents`, {
headers: { headers: {
'Authorization': config.apiKey, 'Authorization': config.apiKey,
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@ -84,37 +94,37 @@ export const searchDocumesoDocuments = async (externalId?: string): Promise<Docu
// If externalId is provided, filter by it // If externalId is provided, filter by it
if (externalId) { if (externalId) {
return response.documents.filter(doc => doc.externalId === externalId); return response.documents.filter((doc: DocumensoDocument) => doc.externalId === externalId);
} }
return response.documents; return response.documents;
} catch (error) { } catch (error) {
console.error('Failed to search Documeso documents:', error); console.error('Failed to search Documenso documents:', error);
throw error; throw error;
} }
}; };
// Get document by external ID (e.g., 'loi-94') // Get document by external ID (e.g., 'loi-94')
export const getDocumesoDocumentByExternalId = async (externalId: string): Promise<DocumesoDocument | null> => { export const getDocumensoDocumentByExternalId = async (externalId: string): Promise<DocumensoDocument | null> => {
const documents = await searchDocumesoDocuments(externalId); const documents = await searchDocumensoDocuments(externalId);
return documents.length > 0 ? documents[0] : null; return documents.length > 0 ? documents[0] : null;
}; };
// Check signature status for a document // Check signature status for a document
export const checkDocumentSignatureStatus = async (documentId: number): Promise<{ export const checkDocumentSignatureStatus = async (documentId: number): Promise<{
documentStatus: string; documentStatus: string;
unsignedRecipients: DocumesoRecipient[]; unsignedRecipients: DocumensoRecipient[];
signedRecipients: DocumesoRecipient[]; signedRecipients: DocumensoRecipient[];
clientSigned: boolean; clientSigned: boolean;
allSigned: boolean; allSigned: boolean;
}> => { }> => {
const document = await getDocumesoDocument(documentId); const document = await getDocumensoDocument(documentId);
const unsignedRecipients = document.recipients.filter(r => r.signingStatus === 'NOT_SIGNED'); const unsignedRecipients = document.recipients.filter((r: DocumensoRecipient) => r.signingStatus === 'NOT_SIGNED');
const signedRecipients = document.recipients.filter(r => r.signingStatus === 'SIGNED'); const signedRecipients = document.recipients.filter((r: DocumensoRecipient) => r.signingStatus === 'SIGNED');
// Check if client (signingOrder = 1) has signed // Check if client (signingOrder = 1) has signed
const clientRecipient = document.recipients.find(r => r.signingOrder === 1); const clientRecipient = document.recipients.find((r: DocumensoRecipient) => r.signingOrder === 1);
const clientSigned = clientRecipient ? clientRecipient.signingStatus === 'SIGNED' : false; const clientSigned = clientRecipient ? clientRecipient.signingStatus === 'SIGNED' : false;
const allSigned = unsignedRecipients.length === 0; const allSigned = unsignedRecipients.length === 0;
@ -129,7 +139,7 @@ export const checkDocumentSignatureStatus = async (documentId: number): Promise<
}; };
// Get recipients who need to sign (excluding client) // Get recipients who need to sign (excluding client)
export const getRecipientsToRemind = async (documentId: number): Promise<DocumesoRecipient[]> => { export const getRecipientsToRemind = async (documentId: number): Promise<DocumensoRecipient[]> => {
const status = await checkDocumentSignatureStatus(documentId); const status = await checkDocumentSignatureStatus(documentId);
// Only remind if client has signed // Only remind if client has signed
@ -138,16 +148,16 @@ export const getRecipientsToRemind = async (documentId: number): Promise<Documes
} }
// Return unsigned recipients with signingOrder > 1 // Return unsigned recipients with signingOrder > 1
return status.unsignedRecipients.filter(r => r.signingOrder > 1); return status.unsignedRecipients.filter((r: DocumensoRecipient) => r.signingOrder > 1);
}; };
// Format recipient name for emails // Format recipient name for emails
export const formatRecipientName = (recipient: DocumesoRecipient): string => { export const formatRecipientName = (recipient: DocumensoRecipient): string => {
const firstName = recipient.name.split(' ')[0]; const firstName = recipient.name.split(' ')[0];
return firstName; return firstName;
}; };
// Get signing URL for a recipient // Get signing URL for a recipient
export const getSigningUrl = (recipient: DocumesoRecipient): string => { export const getSigningUrl = (recipient: DocumensoRecipient): string => {
return recipient.signingUrl; return recipient.signingUrl;
}; };