Initial commit: Vuelato - buscador de vuelos
Some checks failed
ci / ci (22, ubuntu-latest) (push) Has been cancelled

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>
This commit is contained in:
Alejandro Martinez
2026-04-10 23:37:06 +02:00
commit b8906efc80
122 changed files with 37809 additions and 0 deletions

93
CLAUDE.md Normal file
View File

@@ -0,0 +1,93 @@
# CLAUDE.md — Guia para trabajar en Vuelato
## Comandos rapidos
```bash
pnpm dev # Dev server (localhost:3000)
pnpm supabase:up # Levantar Supabase Docker
pnpm supabase:down # Parar Supabase
pnpm supabase:reset # Reset completo (borra datos)
pnpm lint # ESLint
pnpm typecheck # Vue type checking
```
## Estructura del proyecto
```
app/
pages/ # 10 paginas (Nuxt file-based routing)
components/ # 27 componentes organizados por dominio (auth/, search/, results/, detail/, map/, inspiration/)
composables/ # 9 composables (useFlightSearch, useAuth, useWatchlist, etc.)
assets/css/ # Tailwind CSS
server/
api/ # 13 endpoints Nitro (proxy Flightics + sync + flight-info + airlines)
utils/flightics.ts # Cliente API Flightics completo (tipos + 9 funciones)
utils/wikidata.ts # Cliente SPARQL Wikidata (aerolineas)
supabase/
migrations/ # SQL init (tablas cache + usuario + RLS + trigger)
volumes/db/ # roles.sql, jwt.sql (config Supabase internal)
volumes/api/ # kong.yml (API gateway config)
docker-compose.yml # Supabase stack completo (6 servicios)
```
## Convenciones
### Nuxt 4
- El directorio raiz de la app es `app/` (no src/).
- Los server utils (`server/utils/`) se auto-importan en server routes. **No usar** `import ... from '~/server/utils/...'` — causa error porque `~` apunta a `app/`.
- Componentes se auto-importan con prefijo de directorio: `search/AirportInput.vue` -> `<SearchAirportInput />`.
### Componentes
- Usar `@nuxt/ui` v4 components (UCard, UButton, UInput, UBadge, UFormField, UPopover, etc.).
- Iconos: `@iconify-json/lucide` con prefijo `i-lucide-*`.
- No usar emojis en la UI.
- Idioma de la UI: espanol (es).
### Composables
- Cada composable maneja su propio estado reactivo.
- Los composables de usuario (useWatchlist, useRecentSearches, useUserPreferences) hacen watch del user y cargan/limpian datos automaticamente al login/logout.
- `useAirlineNames` es un cache reactivo global — aprende nombres de aerolineas de cualquier respuesta API y los resuelve en la vista.
### Server routes
- Los endpoints de Flightics son proxy directos (sin cache, excepto locations y countries que cachean 24h).
- `defineCachedEventHandler` para cachear respuestas (flight-info: 5 min, locations/countries/airlines: 24h).
- Usar `serverSupabaseServiceRole` para operaciones server-side que necesitan bypass de RLS (ej: sync/locations, sync/airlines).
### Base de datos
- Tablas publicas (airports, countries, airlines, etc.) accesibles por `anon` y `authenticated` (SELECT).
- Tablas de usuario protegidas con RLS: `auth.uid() = user_id`.
- Trigger `on_auth_user_created` crea profile automaticamente.
- Para resetear: `pnpm supabase:reset` (borra volumen Docker y reinicia).
- Para re-sincronizar aeropuertos: `curl -X POST http://localhost:3000/api/sync/locations`.
- Para re-sincronizar aerolineas: `curl -X POST http://localhost:3000/api/sync/airlines` (datos de Wikidata: IATA, ICAO, nombre, logo).
## Gotchas
### Flightics API
- La busqueda requiere polling: primera respuesta tiene `notComplete: true`. El composable hace hasta 3 rondas automaticamente.
- `company.name` viene vacio en rondas de polling posteriores. Se resuelve via `useAirlineNames` (cache reactivo).
- Fechas vacias en el payload causan 400. `useFlightSearch.buildPayload` genera fechas por defecto (hoy + 30 dias).
- `getRouteFlights` devuelve trips ida+vuelta, no vuelos individuales.
- `getInspirations` puede devolver vacio para aeropuertos pequenos — es normal.
### Supabase Docker
- La imagen `supabase/postgres` crea roles internos automaticamente. Los SQL de init solo ajustan passwords.
- Si GoTrue falla al arrancar, verificar que el volumen este limpio: `docker compose down -v && docker compose up -d`.
- Studio corre en puerto 3100 (no 3000, que es Nuxt).
- Meta corre en 8085 (remapeado de 8080 que suele estar ocupado).
### Frontend
- `AirportInput` busca sin acentos (normaliza NFD). "malaga" encuentra "Malaga".
- Modo `multiple` en AirportInput: badges + input. Backspace borra ultimo. Enter/click selecciona.
- Leaflet se renderiza solo client-side (`<ClientOnly>`). Requiere `import 'leaflet/dist/leaflet.css'`.
- El `v-if="seg.company.name"` falla con string vacio — usar `useAirlineNames().resolve()` en su lugar.
## Variables de entorno
```bash
SUPABASE_URL=http://localhost:8000 # Kong gateway
SUPABASE_KEY=eyJ... # anon key (demo JWT)
SUPABASE_SERVICE_ROLE_KEY=eyJ... # service_role key (para server-side)
```
Las keys demo estan en `.env.example`. Para produccion, generar nuevas con un JWT_SECRET propio.