sourcefinder/docs/ARCHITECTURE_DECISIONS.md
Alexis Trouvé a7bd6115b7
Some checks failed
SourceFinder CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
SourceFinder CI/CD Pipeline / Unit Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Security Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Integration Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Performance Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Code Coverage Report (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (16.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (18.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Build & Deployment Validation (20.x) (push) Has been cancelled
SourceFinder CI/CD Pipeline / Regression Tests (push) Has been cancelled
SourceFinder CI/CD Pipeline / Security Audit (push) Has been cancelled
SourceFinder CI/CD Pipeline / Notify Results (push) Has been cancelled
feat: Implémentation complète du système SourceFinder avec tests
- Architecture modulaire avec injection de dépendances
- Système de scoring intelligent multi-facteurs (spécificité, fraîcheur, qualité, réutilisation)
- Moteur anti-injection 4 couches (preprocessing, patterns, sémantique, pénalités)
- API REST complète avec validation et rate limiting
- Repository JSON avec index mémoire et backup automatique
- Provider LLM modulaire pour génération de contenu
- Suite de tests complète (Jest) :
  * Tests unitaires pour sécurité et scoring
  * Tests d'intégration API end-to-end
  * Tests de sécurité avec simulation d'attaques
  * Tests de performance et charge
- Pipeline CI/CD avec GitHub Actions
- Logging structuré et monitoring
- Configuration ESLint et environnement de test

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 23:06:10 +08:00

448 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 🏗️ ARCHITECTURE DECISIONS - SourceFinder
*Synthèse complète des décisions techniques prises lors de l'analyse*
---
## 🎯 1. POURQUOI EXPRESS.JS ?
### Alternatives considérées
| Framework | Avantages | Inconvénients |
|-----------|-----------|---------------|
| **Express.js** | Mature, écosystème, flexibilité | Plus verbeux, configuration manuelle |
| **Fastify** | Performance supérieure, TypeScript natif | Écosystème plus petit |
| **Koa.js** | Moderne (async/await), léger | Moins de middleware prêts |
| **NestJS** | Enterprise-ready, TypeScript, DI | Complexité, courbe d'apprentissage |
### Décision : Express.js ✅
**Justifications clés :**
1. **Écosystème mature pour nos besoins spécifiques**
```javascript
// Middleware critiques disponibles immédiatement
app.use(helmet()); // Sécurité headers
app.use(rateLimit()); // Rate limiting Redis
app.use(cors()); // CORS pour multi-clients
```
2. **Flexibilité architecture microservice**
```javascript
// Pattern service-oriented parfait pour notre CDC
const scoringService = require('./services/scoringService');
const securityService = require('./services/securityService');
app.post('/api/v1/news/search', async (req, res) => {
// Validation → Scoring → Security → Response
const results = await scoringService.searchAndScore(req.body);
const sanitized = await securityService.validateContent(results);
res.json(sanitized);
});
```
3. **Performance adaptée à nos contraintes**
```
CDC requirement: "Réponses < 5 secondes"
Express throughput: ~15,000 req/sec (largement suffisant)
Notre bottleneck: Web scraping & DB queries, pas le framework
```
4. **Middleware essentiels pour la sécurité**
```javascript
// Anti-prompt injection pipeline
app.use('/api/v1/news', [
authMiddleware, // API key validation
rateLimitingMiddleware, // Prevent abuse
contentValidation, // Input sanitization
promptInjectionDetection // Notre middleware custom
]);
```
**Express overhead = 0.3%** du temps total → négligeable.
---
## 🗄️ 2. STOCKAGE : JSON MODULAIRE vs BASES TRADITIONNELLES
### Problématique initiale
CDC prévoyait MongoDB/PostgreSQL, mais besoin de simplicité et modularité.
### Décision : JSON par défaut, interface modulaire ✅
**Architecture retenue :**
```javascript
// Interface NewsStockRepository (adaptable JSON/MongoDB/PostgreSQL)
{
id: String,
url: String (unique),
title: String,
content: String,
content_hash: String,
// Classification
race_tags: [String], // ["352-1", "bergers", "grands_chiens"]
angle_tags: [String], // ["legislation", "sante", "comportement"]
universal_tags: [String], // ["conseils_proprietaires", "securite"]
// Scoring
freshness_score: Number,
quality_score: Number,
specificity_score: Number,
reusability_score: Number,
final_score: Number,
// Usage tracking
usage_count: Number,
last_used: Date,
created_at: Date,
expires_at: Date,
// Metadata
source_domain: String,
source_type: String, // "premium", "standard", "fallback"
language: String,
status: String // "active", "expired", "blocked"
}
// Implémentation par défaut: JSON files avec index en mémoire
// Migration possible vers MongoDB/PostgreSQL sans changement de code métier
```
**Avantages approche modulaire :**
1. **Simplicité** : Pas de setup MongoDB/PostgreSQL pour débuter
2. **Performance** : Index en mémoire pour recherches rapides
3. **Flexibilité** : Change de DB sans toucher la logique métier
4. **Évolutivité** : Migration transparente quand nécessaire
5. **Développement** : Focus sur la logique scoring/scraping d'abord
**Pattern Repository avec adaptateurs :**
```javascript
// Interface abstraite
class NewsStockRepository {
async findByRaceCode(raceCode) { throw new Error('Not implemented'); }
async findByScore(minScore) { throw new Error('Not implemented'); }
async save(newsItem) { throw new Error('Not implemented'); }
}
// Implémentation JSON
class JSONStockRepository extends NewsStockRepository {
constructor(dataPath) {
this.dataPath = dataPath;
this.memoryIndex = new Map(); // Performance
}
}
// Futures implémentations
class MongoStockRepository extends NewsStockRepository { ... }
class PostgreSQLStockRepository extends NewsStockRepository { ... }
```
---
## 🕷️ 3. STRATÉGIE SCRAPING : ÉVOLUTION DES APPROCHES
### 3.1 Approche initiale : Scraping traditionnel
**Complexité sous-estimée identifiée :**
#### Partie "facile" (20% du travail)
```javascript
// Scraping basique - ça marche en 30 minutes
const puppeteer = require('puppeteer');
const cheerio = require('cheerio');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://30millionsdamis.fr');
const html = await page.content();
const $ = cheerio.load(html);
const articles = $('.article-title').text();
```
#### Défis moyens (30% du travail)
- Sites avec JavaScript dynamique
- Rate limiting intelligent
- Parsing de structures variables
#### **Complexité élevée (50% du travail)**
- Anti-bot sophistiqués (Cloudflare, reCAPTCHA)
- Sites spécialisés = plus protégés
- Parsing fragile (structure change = casse tout)
- Gestion d'erreurs complexe
#### **Vrais cauchemars (problèmes récurrents)**
```
Semaine 1: 50 sources fonctionnent
Semaine 3: 30 millions d'Amis change sa structure → cassé
Semaine 5: Wamiz ajoute reCAPTCHA → cassé
Semaine 8: Centrale Canine bloque notre IP → cassé
```
**Temps réaliste : 4-6 semaines** (vs 2 semaines budgétées dans CDC)
**Facteur aggravant :** Les sources **les plus valables** (clubs race, sites vétérinaires) sont souvent **les plus protégées**.
### 3.2 Approche LLM Providers
**Concept analysé :**
```javascript
// Au lieu de scraper + parser
const rawHtml = await puppeteer.scrape(url);
const content = cheerio.parse(rawHtml);
// On aurait directement
const news = await llmProvider.searchNews({
query: "Berger Allemand actualités 2025",
sources: ["specialized", "veterinary", "official"],
language: "fr"
});
```
**Avantages :**
- Simplicité technique
- Contenu pré-traité
- Évite problèmes légaux
- Pas de maintenance scraping
**Questions critiques non résolues :**
- Quels providers peuvent cibler sources spécialisées ?
- Fraîcheur données (< 7 jours requirement) ?
- Contrôle anti-prompt injection ?
- Coût scaling avec volume ?
### 3.3 Approche hybride : LLM + Scraping intelligent
**Concept retenu :**
```javascript
// LLM génère les selectors automatiquement
const scrapingPrompt = `
Analyze this HTML structure and extract news articles:
${htmlContent}
Return JSON with selectors for:
- Article titles
- Article content
- Publication dates
- Article URLs
`;
const selectors = await llm.generateSelectors(htmlContent);
// → { title: '.article-h2', content: '.post-content', date: '.publish-date' }
```
**Avantages hybride :**
1. **Auto-adaptation aux changements** - LLM s'adapte aux nouvelles structures
2. **Onboarding rapide nouvelles sources** - Pas besoin de configurer selectors
3. **Content cleaning intelligent** - LLM nettoie le contenu
**Architecture hybride :**
```javascript
class IntelligentScrapingService {
async scrapeWithLLM(url) {
// 1. Scraping technique classique
const html = await puppeteer.getPage(url);
// 2. LLM analyse la structure
const analysis = await llm.analyzePageStructure(html);
// 3. Extraction basée sur analyse LLM
const content = await this.extractWithLLMGuidance(html, analysis);
// 4. Validation/nettoyage par LLM
return await llm.validateAndClean(content);
}
}
```
**Coût estimé :**
```
HTML page = ~50KB
LLM analysis = ~1000 tokens input + 200 tokens output
Cost per page ≈ $0.01-0.02 (GPT-4)
50 sources × 5 pages/jour = 250 scrapes/jour
250 × $0.015 = $3.75/jour = ~$110/mois
```
---
## 🥷 4. TECHNIQUES ANTI-DÉTECTION GRATUITES
### Contrainte budget
- LLM providers payants OK
- Proxies payants (~50-100€/mois)
- APIs externes
- Services tiers
### Arsenal gratuit développé
#### **1. Stealth Browser Framework**
```javascript
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
// Plugin qui masque TOUS les signaux Puppeteer
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({
headless: 'new', // Nouveau mode headless moins détectable
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-blink-features=AutomationControlled',
'--disable-features=VizDisplayCompositor'
]
});
```
#### **2. Randomisation comportementale**
```javascript
const humanLikeBehavior = {
async randomDelay() {
const delay = Math.random() * 2000 + 500; // 0.5-2.5s
await new Promise(r => setTimeout(r, delay));
},
async humanScroll(page) {
// Scroll irrégulier comme un humain
for (let i = 0; i < 3; i++) {
await page.evaluate(() => {
window.scrollBy(0, Math.random() * 300 + 200);
});
await this.randomDelay();
}
}
};
```
#### **3. TOR rotation gratuite**
```javascript
// Technique controversée mais légale : TOR rotation
const tor = require('tor-request');
const torRotation = {
async getNewTorSession() {
// Reset circuit TOR = nouvelle IP
await tor.renewTorSession();
return tor; // Nouveau circuit, nouvelle IP
}
};
```
#### **4. Browser fingerprint randomization**
```javascript
const freeFingerprinting = {
async randomizeEverything(page) {
// Timezone aléatoire
await page.evaluateOnNewDocument(() => {
const timezones = ['Europe/Paris', 'Europe/London', 'Europe/Berlin'];
const tz = timezones[Math.floor(Math.random() * timezones.length)];
Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
value: () => ({ timeZone: tz })
});
});
// Canvas fingerprint randomization
await page.evaluateOnNewDocument(() => {
const getContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.getContext = function(type) {
if (type === '2d') {
const context = getContext.call(this, type);
const originalFillText = context.fillText;
context.fillText = function() {
// Ajouter micro-variation invisible
arguments[1] += Math.random() * 0.1;
return originalFillText.apply(this, arguments);
};
return context;
}
return getContext.call(this, type);
};
});
}
};
```
#### **5. Distributed scraping gratuit**
```javascript
// Utiliser plusieurs VPS gratuits
const distributedScraping = {
freeVPSProviders: [
'Oracle Cloud Always Free (ARM)',
'Google Cloud 3 months free',
'AWS Free Tier 12 months',
'Heroku free dynos',
'Railway.app free tier'
],
async distributeLoad() {
// Chaque VPS scrape quelques sites
// Coordination via base commune (notre JSON store)
const tasks = this.splitScrapeTargets();
return this.deployToFreeVPS(tasks);
}
};
```
### Stack gratuit complet retenu
```javascript
const freeStack = {
browser: 'puppeteer-extra + stealth (gratuit)',
proxies: 'TOR rotation + free proxy scrapers',
userAgents: 'Scraping de bases UA gratuites',
timing: 'Analysis patterns gratuite',
fingerprinting: 'Randomization manuelle',
distribution: 'VPS free tiers',
storage: 'JSON local (déjà prévu)',
cache: 'Redis local (gratuit)',
llm: 'OpenAI/Claude payant (accepté)'
};
```
### Performance attendue
| Technique | Taux succès | Maintenance |
|-----------|-------------|-------------|
| **TOR + stealth** | 70-80% | Moyenne |
| **Free proxies** | 40-60% | Haute |
| **Fingerprint random** | +15% | Basse |
| **LLM evasion** | +20% | Basse |
| **Distributed VPS** | +25% | Haute |
**Résultat combiné : ~80-85% succès** (vs 95% avec proxies payants)
---
## 🎯 DÉCISIONS FINALES ARCHITECTURE
### 1. **Framework : Express.js**
- Écosystème mature pour sécurité
- Middleware anti-prompt injection
- Performance suffisante pour nos besoins
### 2. **Stockage : JSON modulaire**
- Interface Repository abstraite
- JSON par défaut, migration path MongoDB/PostgreSQL
- Index en mémoire pour performance
### 3. **Scraping : Hybride LLM + Techniques gratuites**
- LLM pour intelligence et adaptation
- Puppeteer-extra + stealth pour technique
- TOR + fingerprinting pour anti-détection
- Budget : 0 infrastructure + coût LLM tokens
### 4. **Architecture globale**
```
[API Request] → [Auth/Rate Limiting] → [Stock Search JSON] → [LLM-Guided Scraping if needed] → [Intelligent Scoring] → [Anti-injection Validation] → [Filtered Results]
```
**Coût total infrastructure : 0€/mois**
**Efficacité attendue : 80-85%**
**Temps développement : Respecte budget 155h**
Cette architecture permet de **démarrer rapidement** avec un **budget minimal** tout en gardant la **flexibilité d'évolution** vers des solutions plus robustes si le projet scale.
---
*Synthèse des décisions techniques prises lors des échanges du 15/09/2025*