Files
vuelato/plans/search-queue-tracking.md
Alejandro Martinez b8906efc80
Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled
Initial commit: Vuelato - buscador de vuelos
Nuxt 4 + Supabase + Flightics API. Incluye búsqueda de vuelos,
inspiraciones, watchlist, tracking de precios y mapa interactivo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:37:06 +02:00

131 lines
4.3 KiB
Markdown

# Plan: Sistema de cola de busquedas y seguimiento de precios
## Contexto
Vuelato tiene watchlist manual (check precio uno a uno) y busquedas recientes, pero no hay:
- Busquedas automaticas en segundo plano
- Historico de precios con graficos
- Cache de resultados entre usuarios
El usuario quiere un sistema completo: definir busquedas recurrentes desde la UI, ver fluctuaciones de precio en tablas/graficos, y ademas una capa de cache que evite llamadas duplicadas a Flightics (TTL 1h) reutilizable tanto por el worker como por busquedas manuales.
---
## 1. Esquema de base de datos
**Archivo: `supabase/migrations/00002_search_queue.sql`**
### Tabla `search_cache` — Cache de resultados (publica, sin RLS)
- `id` SERIAL PRIMARY KEY
- `params_hash` TEXT UNIQUE — SHA-256 del JSON de SearchParams normalizado
- `search_params` JSONB — el payload completo
- `trips` JSONB — array completo de trips devueltos
- `cheapest_price` NUMERIC(10,2)
- `total_results` INTEGER
- `fetched_at` TIMESTAMPTZ DEFAULT now()
### Tabla `tracked_searches` — Busquedas recurrentes (RLS por user_id)
- `id` UUID PK DEFAULT gen_random_uuid()
- `user_id` UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE
- `name` TEXT NOT NULL
- `search_params` JSONB NOT NULL
- `route_summary` TEXT NOT NULL
- `interval_hours` INTEGER NOT NULL DEFAULT 24
- `is_active` BOOLEAN DEFAULT true
- `next_run_at` TIMESTAMPTZ DEFAULT now()
- `last_run_at` TIMESTAMPTZ
- `run_count` INTEGER DEFAULT 0
- `last_error` TEXT
- `expires_at` TIMESTAMPTZ
- `created_at` TIMESTAMPTZ DEFAULT now()
### Tabla `search_runs` — Log de ejecuciones (RLS via tracked_search_id)
- `id` UUID PK
- `tracked_search_id` UUID NOT NULL REFERENCES tracked_searches(id) ON DELETE CASCADE
- `status` TEXT DEFAULT 'pending'
- `cheapest_price` NUMERIC(10,2)
- `total_trips_found` INTEGER DEFAULT 0
- `top_trips` JSONB
- `from_cache` BOOLEAN DEFAULT false
- `error_message` TEXT
- `started_at` TIMESTAMPTZ
- `completed_at` TIMESTAMPTZ
- `created_at` TIMESTAMPTZ DEFAULT now()
### Tabla `price_snapshots` — Datos para graficos (RLS via tracked_search_id)
- `id` UUID PK
- `tracked_search_id` UUID NOT NULL REFERENCES tracked_searches(id) ON DELETE CASCADE
- `search_run_id` UUID NOT NULL REFERENCES search_runs(id) ON DELETE CASCADE
- `cheapest_price` NUMERIC(10,2) NOT NULL
- `avg_price` NUMERIC(10,2)
- `median_price` NUMERIC(10,2)
- `total_results` INTEGER DEFAULT 0
- `recorded_at` TIMESTAMPTZ DEFAULT now()
---
## 2. Cache de busquedas — Flujo integrado
### Hash de parametros (`server/utils/search-hash.ts`)
- Normaliza SearchParams (ordena keys, elimina `_poll`), genera SHA-256
- Compartido entre `/api/search` y el worker
### Endpoint `/api/search` modificado (cache-aware)
1. Calcular params_hash
2. Buscar en search_cache WHERE params_hash = hash AND fetched_at > now() - 1 hour
3. Si hay cache fresco → devolver trips del cache
4. Si no → llamar a Flightics, guardar en search_cache, devolver
### Worker usa el mismo cache
- Lee cache antes de llamar a Flightics
- Escribe al cache tras cada busqueda nueva
---
## 3. Worker (`server/plugins/search-worker.ts`)
- Nitro plugin con `setInterval(processQueue, 60_000)`
- Mutex para evitar solapamiento
- Procesa hasta 5 jobs por ciclo con 10s entre cada uno
- Usa `server/utils/supabase-admin.ts` (cliente Supabase con service_role sin H3 event)
---
## 4. API Endpoints (`server/api/tracking/`)
| Archivo | Metodo | Funcion |
|---------|--------|---------|
| `index.get.ts` | GET | Listar tracked_searches con ultimo snapshot |
| `index.post.ts` | POST | Crear tracked_search |
| `[id].patch.ts` | PATCH | Editar configuracion |
| `[id].delete.ts` | DELETE | Eliminar + cascade |
| `[id]/history.get.ts` | GET | Price snapshots para graficos |
| `[id]/runs.get.ts` | GET | Log de ejecuciones |
---
## 5. Composable (`app/composables/useTrackedSearches.ts`)
Patron identico a useWatchlist.ts con watch(user) para auto-load/cleanup.
---
## 6. UI: vue-chartjs + chart.js
## 7. Componentes: tracking/PriceChart, TrackedSearchCard, CreateTrackingForm, RunHistory, TrackingConfig
## 8. Paginas: /tracking (dashboard) + /tracking/[id] (detalle con grafico)
## 9. Integracion: boton en /results, icono en recientes, link en nav
---
## Orden de implementacion
### Fase 1: DB + Cache
### Fase 2: Worker
### Fase 3: API tracking
### Fase 4: Composable + UI basica
### Fase 5: Graficos y detalle
### Fase 6: Integracion