port-nimara-client-portal/server/api/interests/duplicates/find.ts

96 lines
3.1 KiB
TypeScript

import { requireSalesOrAdmin } from '~/server/utils/auth';
import { getNocoDbConfiguration } from '~/server/utils/nocodb';
import { logAuditEvent } from '~/server/utils/audit-logger';
import { findDuplicates, createInterestConfig } from '~/server/utils/duplicate-detection';
export default defineEventHandler(async (event) => {
console.log('[INTERESTS] Find duplicates request');
try {
// Require sales or admin access for duplicate detection
await requireSalesOrAdmin(event);
const query = getQuery(event);
const threshold = query.threshold ? parseFloat(query.threshold as string) : 0.8;
const dateRange = query.dateRange ? parseInt(query.dateRange as string) : 365; // Default 1 year
// Get all interests from NocoDB
const config = getNocoDbConfiguration();
const interestTableId = "mbs9hjauug4eseo"; // Interest table ID
let url = `${config.url}/api/v2/tables/${interestTableId}/records`;
// Add date filtering if specified (include records without Created At)
if (dateRange && dateRange > 0) {
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - dateRange);
// Include records without Created At OR within date range
const dateFilter = `((Created At,gte,${cutoffDate.toISOString()}),or,(Created At,is,null))`;
url += `?where=${encodeURIComponent(dateFilter)}`;
}
const response = await $fetch(url, {
headers: {
'xc-token': config.token
},
params: {
limit: 5000 // Get a large batch for duplicate detection
}
}) as any;
const interests = response.list || [];
console.log('[INTERESTS] Analyzing', interests.length, 'interests for duplicates');
// Find duplicate groups using the new centralized utility
const duplicateConfig = createInterestConfig();
const duplicateGroups = findDuplicates(interests, duplicateConfig);
// Convert to the expected format
const formattedGroups = duplicateGroups.map(group => ({
id: group.id,
interests: group.items,
matchReason: group.matchReason,
confidence: group.confidence,
masterCandidate: group.masterCandidate
}));
console.log('[INTERESTS] Found', formattedGroups.length, 'duplicate groups');
// Log the audit event
await logAuditEvent(event, 'FIND_INTEREST_DUPLICATES', 'interest', {
changes: {
totalInterests: interests.length,
duplicateGroups: formattedGroups.length,
threshold,
dateRange
}
});
return {
success: true,
data: {
duplicateGroups: formattedGroups,
totalInterests: interests.length,
duplicateCount: formattedGroups.reduce((sum, group) => sum + group.interests.length, 0),
threshold,
dateRange
}
};
} catch (error: any) {
console.error('[INTERESTS] Failed to find duplicates:', error);
if (error.statusCode === 403) {
return {
success: false,
error: 'Insufficient permissions. Sales or admin access required.'
};
}
return {
success: false,
error: 'Failed to find duplicates'
};
}
});