feat: Refactor duplicate handling in InterestDuplicateNotificationBanner and update merge API for better access control
This commit is contained in:
189
server/api/interests/duplicates/merge.ts
Normal file
189
server/api/interests/duplicates/merge.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { requireSalesOrAdmin } from '~/server/utils/auth';
|
||||
import { getNocoDbConfiguration } from '~/server/utils/nocodb';
|
||||
import { logAuditEvent } from '~/server/utils/audit-logger';
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
console.log('[INTERESTS] Merge duplicates request');
|
||||
|
||||
let body: any;
|
||||
|
||||
try {
|
||||
// Require sales or admin access
|
||||
const session = await requireSalesOrAdmin(event);
|
||||
const userId = session.user?.id || 'unknown';
|
||||
const userEmail = session.user?.email || 'unknown';
|
||||
|
||||
body = await readBody(event);
|
||||
const { masterId, duplicateIds, mergeData } = body;
|
||||
|
||||
if (!masterId || !duplicateIds || !Array.isArray(duplicateIds) || duplicateIds.length === 0) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Invalid merge request. Master ID and duplicate IDs required.'
|
||||
});
|
||||
}
|
||||
|
||||
console.log('[INTERESTS] Merging', duplicateIds.length, 'duplicates into master interest', masterId);
|
||||
|
||||
// Get NocoDB configuration
|
||||
const config = getNocoDbConfiguration();
|
||||
const interestTableId = "mbs9hjauug4eseo"; // Interest table ID
|
||||
|
||||
// First, get all interests involved
|
||||
const allIds = [masterId, ...duplicateIds];
|
||||
const interestsToMerge: any[] = [];
|
||||
|
||||
for (const id of allIds) {
|
||||
try {
|
||||
const interest = await $fetch(`${config.url}/api/v2/tables/${interestTableId}/records/${id}`, {
|
||||
headers: {
|
||||
'xc-token': config.token
|
||||
}
|
||||
});
|
||||
interestsToMerge.push(interest);
|
||||
} catch (error) {
|
||||
console.error(`[INTERESTS] Failed to fetch interest ${id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
if (interestsToMerge.length < 2) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Could not find enough interests to merge'
|
||||
});
|
||||
}
|
||||
|
||||
const masterInterest = interestsToMerge.find(i => i.Id === parseInt(masterId));
|
||||
if (!masterInterest) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Master interest not found'
|
||||
});
|
||||
}
|
||||
|
||||
// Log the action before making changes
|
||||
await logAuditEvent(event, 'MERGE_INTEREST_DUPLICATES', 'interest', {
|
||||
resourceId: masterId,
|
||||
changes: {
|
||||
duplicateIds,
|
||||
mergeData,
|
||||
originalInterests: interestsToMerge
|
||||
}
|
||||
});
|
||||
|
||||
// Update master interest with merged data if provided
|
||||
if (mergeData && Object.keys(mergeData).length > 0) {
|
||||
const updateData: any = { Id: parseInt(masterId) };
|
||||
|
||||
// Copy allowed fields
|
||||
const allowedFields = [
|
||||
'Full Name',
|
||||
'Email Address',
|
||||
'Phone Number',
|
||||
'Address',
|
||||
'Extra Comments',
|
||||
'Berth Size Desired',
|
||||
'Sales Process Level',
|
||||
'EOI Status',
|
||||
'Contract Status',
|
||||
'Lead Category'
|
||||
];
|
||||
|
||||
allowedFields.forEach(field => {
|
||||
if (mergeData[field] !== undefined) {
|
||||
updateData[field] = mergeData[field];
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(updateData).length > 1) { // More than just Id
|
||||
console.log('[INTERESTS] Updating master interest with:', updateData);
|
||||
|
||||
await $fetch(`${config.url}/api/v2/tables/${interestTableId}/records`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'xc-token': config.token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: updateData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Delete duplicate interests
|
||||
const deleteResults = [];
|
||||
for (const duplicateId of duplicateIds) {
|
||||
try {
|
||||
console.log('[INTERESTS] Deleting duplicate interest:', duplicateId);
|
||||
|
||||
await $fetch(`${config.url}/api/v2/tables/${interestTableId}/records`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'xc-token': config.token,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: { Id: parseInt(duplicateId) }
|
||||
});
|
||||
|
||||
deleteResults.push({ id: duplicateId, success: true });
|
||||
} catch (error: any) {
|
||||
console.error('[INTERESTS] Failed to delete duplicate:', duplicateId, error);
|
||||
deleteResults.push({
|
||||
id: duplicateId,
|
||||
success: false,
|
||||
error: error.message || 'Delete failed'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all deletes were successful
|
||||
const failedDeletes = deleteResults.filter(r => !r.success);
|
||||
if (failedDeletes.length > 0) {
|
||||
console.warn('[INTERESTS] Some duplicates could not be deleted:', failedDeletes);
|
||||
}
|
||||
|
||||
// Log successful completion
|
||||
await logAuditEvent(event, 'MERGE_INTEREST_DUPLICATES_COMPLETE', 'interest', {
|
||||
resourceId: masterId,
|
||||
changes: {
|
||||
deletedCount: deleteResults.filter(r => r.success).length,
|
||||
failedDeletes,
|
||||
mergeData
|
||||
},
|
||||
status: 'success'
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
masterId,
|
||||
mergedData: mergeData,
|
||||
deletedCount: deleteResults.filter(r => r.success).length,
|
||||
deleteResults,
|
||||
message: `Successfully merged ${deleteResults.filter(r => r.success).length} duplicates into interest ${masterId}`
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('[INTERESTS] Failed to merge duplicates:', error);
|
||||
|
||||
// Log the failure
|
||||
await logAuditEvent(event, 'MERGE_INTEREST_DUPLICATES_FAILED', 'interest', {
|
||||
resourceId: body?.masterId || 'unknown',
|
||||
changes: {
|
||||
error: error.message || 'Unknown error',
|
||||
requestBody: body
|
||||
},
|
||||
status: 'failure',
|
||||
errorMessage: error.message || 'Unknown error'
|
||||
});
|
||||
|
||||
if (error.statusCode) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to merge duplicate interests'
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user