Consulta el estado de las playas de Cantabria en tiempo real: puntuación de condiciones, predicción a 3 días con detalle por mañana y tarde, mareas, índice UV, avisos meteorológicos y estado de la bandera de la Cruz Roja.
Available languages: Español | English
Puedes probar la aplicación aquí: https://playucas.es/
Backend API: https://playas-cantabria.onrender.com
El repositorio, el proyecto de Firebase (
playas-cantabria-front) y el servicio de Render conservan el nombre históricoplayas-cantabria; la marca pública es Playucas.es.
- Portada "la mejor playa para hoy": ranking automático de las playas del catálogo con puntuación 0–100 según condiciones (cielo, viento, bandera…), con las mejores alternativas y aviso claro de que es una recomendación orientativa.
- Listado de playas con búsqueda por nombre o municipio, ordenación A-Z / Z-A, favoritos, resumen de condiciones por playa y tendencia (mejora / empeora).
- Detalle de la playa:
- Predicción a 3 días con selector de día y detalle mañana/tarde: cielo, viento y oleaje.
- Temperatura máxima, del agua y sensación térmica; índice UV con código de colores; avisos meteorológicos con severidad.
- Tira horaria de las próximas horas y aviso de lluvia inminente (nowcast).
- Mareas: horas de pleamar y bajamar, con indicador en tiempo real de si la marea sube o baja.
- Bandera y vigilancia con su operador (Cruz Roja): estado, cobertura y horario, con fecha de actualización.
- Botón Compartir que genera una tarjeta-imagen con la lectura del día, y botón "Cómo llegar" (Google Maps).
- Enlace a webcam cuando la playa dispone de una fiable.
- Distintivo Bandera Azul (ADEAC) con su año de concesión y enlace a la web oficial, en la ficha y como icono en el listado.
- Mapa interactivo (Leaflet/OpenStreetMap) con condiciones, banderas izadas y tu ubicación.
- Páginas de municipios y landings temáticas, prerenderizadas junto al resto de rutas para SEO (sitemap y canónicas incluidos).
- PWA instalable con modo offline parcial: si el backend no responde en 2.5s, el listado se sirve desde un JSON local y se actualiza cuando el servidor contesta.
- Bilingüe: interfaz en español e inglés.
La app agrega información de múltiples fuentes con cadena de fallback:
- AEMET (scraping XML/HTML): Predicción enriquecida a 3 días, mareas, avisos y UV real (fuente principal).
- AEMET OpenData API: Predicción a 2 días como respaldo.
- OpenWeatherMap: Datos meteorológicos, UV estimado y respaldo de la tira horaria.
- Open-Meteo: Precipitación horaria para el nowcast de lluvia.
- Cruz Roja: Estado de la bandera y servicios de socorrismo (scraping).
Caché en memoria con TTL base configurable (1800s por defecto, escalado por hora del día y temporada), ventana stale que sirve el último valor bueno si un proveedor cae, deduplicación singleflight y segundo nivel opcional en Upstash Redis.
Licencias de los datos: la licencia de este repositorio cubre únicamente el código. Los datos de AEMET, OpenWeatherMap, Open-Meteo, Cruz Roja y OpenStreetMap pertenecen a sus respectivas fuentes y se rigen por sus propios términos (atribución obligatoria; ODbL en el caso de OpenStreetMap). Quien despliegue este software asume esas obligaciones frente a cada proveedor de datos.
El motor es agnóstico de región: una región es un directorio de datos en regions/<id>/, con su region.json (nombre, marca, centro del mapa, operadores de bandera) y su catálogo beaches.json. El backend valida cada región al arrancar y monta su API bajo /api/<id>/…; el frontend se construye por región con REACT_APP_REGION=<id>, que inyecta marca, mapa y catálogo de esa región. Cantabria es la región de referencia; regions/asturias/ existe como ejemplo de región contribuida.
El backend sigue una Arquitectura Hexagonal (Puertos y Adaptadores). Las dependencias siempre apuntan hacia adentro: infrastructure → application → domain.
-
Domain(Núcleo)- Entidades:
Beach,Weather,Flag,Tides,BeachForecast,RainNowcast,Sunshine. - Puertos (interfaces):
BeachRepository,WeatherProvider,FlagProvider,TidesProvider. - Casos de uso:
GetAllBeaches,GetBeachById,GetBeachDetails,GetFeaturedBeaches(conBeachScorer),GetRainNowcast. - Sin dependencias de otras capas.
- Entidades:
-
Application(Orquestación)- DTOs:
BeachDTO,BeachDetailsDTO. - Mappers:
BeachMapper,BeachDetailsMapper,FeaturedBeachMapper,LegacyDetailsMapper. - Servicios:
DetailsAssembleryLegacyDetailsAssembler(orquestan la cadena de fallback). - Validación: Esquemas Zod para parámetros de ruta.
- DTOs:
-
Infrastructure(Exterior)- Express: Servidor, rutas, middlewares (CORS, rate limit, errores).
- Proveedores:
AemetBeachWebScraper,AemetBeachForecastProvider,AemetWeatherProvider,OpenWeatherWeatherProvider,OpenMeteoPrecipitationProvider,RedCrossFlagProvideryFlagProviderRouter(despacha por operador de bandera). - Repositorio:
JsonBeachRepository(lee el catálogo JSON de la región). - Caché:
InMemoryCachecon TTL y singleflight, con L2 opcional en Upstash Redis. - DI: Contenedor manual sin framework (
dependencies.ts), uno por región válida.
Capa 1: AemetBeachWebScraper → XML/HTML público de aemet.es (3 días, mañana/tarde, mareas, avisos, UV)
Capa 2: AemetBeachForecastProvider → OpenData API con API key (2 días)
Capa 3: OpenWeatherWeatherProvider → OpenWeather API (temp, viento, descripción)
Capa 4: GetBeachDetails → AEMET observación ↔ OpenWeather (hedged, el primero que responde gana)
- Lenguaje: TypeScript v5.5
- Entorno de Ejecución: Node.js v20+
- Framework: Express.js v4.19
- Arquitectura: Hexagonal (Puertos y Adaptadores) con DI manual.
- Validación: Zod
- HTTP: Axios
- Scraping: Cheerio
- Codificación: iconv-lite (AEMET sirve ISO-8859-15)
- Tests: Vitest
- Despliegue: Render (principal), Firebase Functions (alternativo)
- Framework: React 18 (Create React App)
- UI Framework: Ionic React
- Lenguaje: TypeScript
- Enrutador: React Router
- Mapas: Leaflet / react-leaflet con OpenStreetMap
- Plataforma Móvil: Capacitor
- Tests: Jest + React Testing Library (suite de caracterización)
- Despliegue Web: Firebase Hosting (un site por región)
playas-cantabria/
├── regions/ # Datos por región: region.json + beaches.json
│ ├── cantabria/
│ └── asturias/
├── backend/
│ └── src/
│ ├── domain/ # Entidades, puertos, servicios de dominio, casos de uso
│ ├── application/ # DTOs, mappers, servicios, validación
│ ├── infrastructure/ # Express, proveedores, caché, DI, repositorios, config
│ └── regions/ # Registro y validación de regiones en runtime
├── frontend/
│ └── src/
│ ├── app/ # Entrada, rutas y tema (variables CSS)
│ ├── pages/ # Portada, listado, detalle, mapa, municipios, landings, legal
│ ├── modules/ # compartir (tarjeta), favoritos, instalación PWA
│ ├── shared/ # i18n (es/en), SEO/prerender, config, geo, formato, UI
│ ├── services/ # Cliente API
│ └── data/ # Respaldo local del catálogo (generado en build)
└── docs/ # Capturas y documentación
- Node.js v20+
- npm (u otro gestor de paquetes)
git clone https://github.com/oscaruiz/playas-cantabria.git
cd playas-cantabria
# Backend
cd backend
npm install
cp .env.example .env
# Rellena .env con tus API keys
# Frontend
cd ../frontend
npm installNecesitas dos terminales:
# Terminal 1 — Backend (http://localhost:4000)
cd backend
npm run dev
# Terminal 2 — Frontend (http://localhost:3000)
cd frontend
npm startcd backend && npm test # Vitest
cd frontend && npm test # Jest + RTL (restaura siempre la región Cantabria)La API es multi-región: todas las rutas van bajo /api/:region/….
| Método | Ruta | Descripción |
|---|---|---|
| GET | /api/:region/beaches |
Listado de todas las playas de la región |
| GET | /api/:region/beaches/featured |
Ranking de condiciones (puntuación por playa) |
| GET | /api/:region/beaches/:id |
Información básica de una playa |
| GET | /api/:region/beaches/:id/details |
Detalle completo: predicción 3 días, mareas, bandera, coordenadas |
Las rutas históricas sin región (/api/beaches…) se mantienen como alias en desuso de Cantabria para los clientes ya instalados. Todo /api está limitado a 60 peticiones/minuto por IP.
# Listado (Cantabria)
curl "http://localhost:4000/api/cantabria/beaches"
# Detalle completo (La Concha de Suances)
curl "http://localhost:4000/api/cantabria/beaches/3908503/details"El endpoint /details consolida datos de AEMET, OpenWeatherMap, Open-Meteo y el operador de banderas e incluye predicción a 3 días con mañana/tarde, mareas (pleamar/bajamar), índice UV, avisos meteorológicos y coordenadas GPS.
| Variable | Descripción | Default |
|---|---|---|
PORT |
Puerto del servidor | 4000 |
AEMET_API_KEY |
Clave API de AEMET OpenData | — |
OPENWEATHER_API_KEY |
Clave API de OpenWeatherMap | — |
CORS_ORIGIN |
Origen CORS permitido | * |
CACHE_TTL_SECONDS |
TTL base de proveedores en segundos (escalado por hora y temporada) | 1800 |
FEATURED_FRESH_TTL_SECONDS |
Tiempo fresco del ranking antes de refrescar en segundo plano | 300 |
FEATURED_STALE_TTL_SECONDS |
Máximo tiempo durante el que se puede servir el último ranking válido | 3600 |
DETAILS_FRESH_TTL_SECONDS |
Tiempo fresco del detalle consolidado | 60 |
DETAILS_STALE_TTL_SECONDS |
Máximo tiempo para servir un detalle previo durante el refresco | 600 |
UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN |
Opcionales; activan la caché de dos niveles que sobrevive a los reinicios | — |
DEBUG_WEATHER |
1 habilita logs detallados de proveedores |
— |
| Variable | Descripción | Default |
|---|---|---|
REACT_APP_API_BASE_URL |
URL del backend | https://playas-cantabria.onrender.com |
REACT_APP_REGION |
Región que sirve este build | cantabria |
REACT_APP_SITE_ORIGIN |
Origen público del despliegue (canónicas, sitemap, manifest) | — |
Las contribuciones son bienvenidas. Si tienes ideas, sugerencias o quieres reportar un error, abre un issue en este repositorio. Antes de enviar un PR, consulta CONTRIBUTING.md.
Este proyecto se distribuye bajo dos licencias a la vez: PolyForm Noncommercial 1.0.0 y PolyForm Shield 1.0.0; hay que cumplir las dos. El código está a la vista: puedes usarlo, estudiarlo, hacer forks y contribuir. No se permite ningún uso comercial del código, ni ofrecer con él un producto que compita con Playucas (aunque sea gratuito). Por estas restricciones el proyecto es source-available, no código abierto según la definición de la OSI.
El nombre, el logo y la identidad visual de "Playucas" no están cubiertos por la licencia del software: consulta TRADEMARK.md.
Este proyecto sigue Semantic Versioning. Actualmente en v2.1.0.
-
Incorporar datos de mareas -
Añadir más playas(51 playas de Cantabria en el catálogo) - Mejorar la arquitectura del frontend (estado, tipos discriminados, caching)
- Publicar OpenAPI/Swagger de la API
- Tests E2E básicos (Playwright) para flujos principales



