# Arquitectura General

## Visión de conjuntos

```
┌─────────────────────────────────────────────────────────────────┐
│                    BROWSER / PWA CLIENT                         │
│   Dashboard EJS  │  Map View  │  Filters  │  Analytics          │
└─────────────────────────┬───────────────────────────────────────┘
                          │ HTTP / REST
┌─────────────────────────▼───────────────────────────────────────┐
│                  EXPRESS 4.18  (port 3000)                      │
│                                                                 │
│  Middleware: express-session, passport, express-mysql-session   │
│  Auth: auth.ts middleware (req.isAuthenticated)                 │
│                                                                 │
│  Routes:                                                        │
│    GET  /              → dashboard/index.ejs                    │
│    GET  /map           → dashboard/map.ejs                      │
│    GET  /filters       → dashboard/filters.ejs                  │
│    GET  /portals       → dashboard/portals.ejs                  │
│    GET  /zones         → dashboard/zones.ejs                    │
│    GET  /analytics     → dashboard/analytics.ejs                │
│    POST /auth/login    → authController                         │
│    GET  /api/properties                                         │
│    POST /api/scrape                                             │
│    ... etc                                                      │
└──────────────┬──────────────────────────────────┬───────────────┘
               │                                  │
    ┌──────────▼─────────┐              ┌─────────▼──────────────┐
    │  PropertyService   │              │  MySQL (XAMPP)         │
    │  ScraperService    │              │  DB: lliria_properties  │
    │  ScoringService    │              │                        │
    │  GeocodingService  │              │  Tables:               │
    │  EmailService      │              │    properties          │
    │  DeduplicationSvc  │              │    portals             │
    └──────────┬─────────┘              │    zones               │
               │                        │    filters             │
    ┌──────────▼─────────────────────┐  │    price_history       │
    │  Scrapers (20 portales)        │  │    scraping_log        │
    │                                │  │    users               │
    │  Cheerio (HTTP/HTML):          │  │    sessions            │
    │    Habitaclia, Kyero,          │  └────────────────────────┘
    │    ThinkSpain, Trovit,         │
    │    Pisos.com, Milanuncios,     │
    │    Yaencontre, Casasapo,       │
    │    Vibbo, Globaliza, Nuroa,    │
    │    EngelVolkers, Tecnocasa,    │
    │    EraSpain, Remax,            │
    │    Inmobiliaria, SpainHouses,  │
    │    Holprop                     │
    │                                │
    │  Playwright (JS dinámico):     │
    │    IdealistaPlaywright         │
    │    FotocasaPlaywright          │
    └────────────────────────────────┘
```

## Stack Tecnológico

| Capa            | Tecnología                     | Versión   |
| --------------- | ------------------------------ | --------- |
| Runtime         | Node.js                        | 22 LTS    |
| Lenguaje        | TypeScript                     | 5.3       |
| Framework web   | Express                        | 4.18      |
| Plantillas      | EJS                            | 3.x       |
| BD              | MySQL + mysql2                 | 8.0 / 3.x |
| Scraping HTTP   | Cheerio + Axios                | —         |
| Scraping JS     | Playwright (Chromium)          | 1.x       |
| Gestor procesos | PM2                            | —         |
| Auth            | Passport.js (local)            | —         |
| Sesiones        | express-mysql-session          | —         |
| Correo          | Nodemailer                     | —         |
| PWA             | Service Worker + manifest.json | —         |

## Flujo de Datos Típico

1. **Scheduler** (schedulerService) activa `scrapeAll()` según cron configurado
2. **PropertyService.scrapeAll()** consulta portales activos en BD → instancia cada scraper
3. Cada **Scraper** llama a `getActiveZones()` (con caché 60s) y construye URLs por municipio
4. Los datos brutos se normalizan (`normalizeType`, `detectMunicipality`) y se geocodifican
5. **DeduplicationService** marca duplicados por título/precio/ubicación similares
6. **ScoringService** asigna puntuación 0-10 (precio, m², habitaciones, antigüedad)
7. Las propiedades se insertan/actualizan en MySQL con historial de precios
8. El **EmailService** comprueba si hay propiedades que coincidan con filtros activos y envía alertas
