2026-04-16-2

This commit is contained in:
Hamza-Ayed
2026-04-16 04:13:55 +03:00
parent 58f06eeba3
commit 47a38d657e
8 changed files with 286 additions and 33 deletions
@@ -40,4 +40,13 @@ export class MapCandidate extends BasePlace {
@Column({ type: 'jsonb', nullable: true })
metadata: any; // For photos, extra contact info, etc.
@Column({ nullable: true })
verified_name: string;
@Column({ type: 'int', default: 0 })
confidence_score: number;
@Column({ type: 'jsonb', nullable: true })
verification_metadata: any;
}
@@ -77,8 +77,13 @@ export class MapRefinementService {
});
const saved = await this.candidateRepository.save(candidate);
this.logger.log(`✅ Success: Candidate ${saved.id} enriched with Gov:${saved.governorate_id}, Neigh:${saved.neighborhood_id}`);
this.logger.log(`✅ Success: Candidate ${saved.id} enriched with Gov:${saved.governorate_id}, Neigh:${spatialData.neigh_id}`);
// Trigger automated verification in background (don't block the response)
this.runExternalVerification(saved.id).catch(err =>
this.logger.error(`❌ Verification failed for ${saved.id}: ${err.message}`)
);
// Return with enriched names for immediate feedback
return {
...saved,
@@ -89,6 +94,67 @@ export class MapRefinementService {
} as any;
}
/**
* Automated verification using Search Engine Metadata Scraping
*/
async runExternalVerification(candidateId: number): Promise<void> {
const candidate = await this.candidateRepository.findOne({ where: { id: candidateId } });
if (!candidate) return;
this.logger.log(`🔍 Starting automated verification for candidate ${candidate.id}...`);
try {
const axios = require('axios');
// Using DuckDuckGo HTML search for coordinates - very stable for scraping names
const searchUrl = `https://duckduckgo.com/html/?q=${candidate.latitude},${candidate.longitude}`;
const response = await axios.get(searchUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
},
timeout: 10000
});
const html = response.data;
const titleMatch = html.match(/<title>([^<]+)<\/title>/);
if (titleMatch) {
let officialName = titleMatch[1].replace(' at DuckDuckGo', '').replace(' - DuckDuckGo', '').trim();
// If the name is just coordinates, it means no POI was specifically identified
const isCoordinates = /^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$/.test(officialName);
if (!isCoordinates && officialName.length > 5) {
candidate.verified_name = officialName;
// Basic confidence: if the user's name is partially in the official name, high trust
const cleanUser = candidate.name.toLowerCase().trim();
const cleanOfficial = officialName.toLowerCase();
if (cleanOfficial.includes(cleanUser) || cleanUser.includes(cleanOfficial)) {
candidate.confidence_score = 90;
} else {
candidate.confidence_score = 60; // Found a name, but didn't match perfectly
}
this.logger.log(`🎯 Verified: Found "${officialName}" near suggestion. Trust: ${candidate.confidence_score}%`);
} else {
candidate.confidence_score = 10; // Only coordinates found
this.logger.log(`⚠️ No specific POI name found for coordinates ${candidate.latitude},${candidate.longitude}`);
}
}
candidate.verification_metadata = {
last_check: new Date().toISOString(),
source: 'DDG_SCRAPE'
};
await this.candidateRepository.save(candidate);
} catch (error) {
this.logger.error(`Automated verification error: ${error.message}`);
}
}
async getCandidates(status?: CandidateStatus): Promise<any[]> {
const query = `
SELECT
+6 -2
View File
@@ -10,8 +10,12 @@ async function bootstrap() {
logger: ['error', 'warn', 'log', 'debug', 'verbose'],
});
// 1. Modern Security Headers (Prevention of XSS, Clickjacking, etc.)
app.enableCors();
// 1. Modern Security Headers & Permissive CORS for Production Dashboard
app.enableCors({
origin: true, // Reflect request origin
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
credentials: true,
});
// 3. Global API prefix
app.setGlobalPrefix('api');