Replace the @astrojs/sitemap integration with two handwritten endpoints under src/pages/ so we can attach a Google News <news:news> block to each aviso detail URL. The integration already emitted the news namespace but never the per-URL blocks, so the project was not eligible for Google News indexing. - src/pages/sitemap-index.xml.ts: single-child sitemapindex pointing at /sitemap-0.xml. - src/pages/sitemap-0.xml.ts: home + huella + one entry per aviso from getAvisosAmbientales. Each aviso carries <news:news> with publication name (fuente), language ca, publication_date (ISO), title and keywords (temas joined). - astro.config.mjs: drop the @astrojs/sitemap integration; remove the dependency from package.json and pnpm-lock.yaml. - e2e: add a test asserting the news namespace and at least one <news:news> block with publication_date and language. 22/22 e2e pass; check and eslint clean.
Ibiza Environmental Watch — frontend
Frontend estático (Astro) para el proyecto de vigilancia ambiental de
Eivissa i Formentera. Consume boib_entradas y noticias_prensa
(es_ambiental = true) directamente de Postgres, en build-time, y
publica HTML estático servido por Caddy. No hay servidor Node en producción.
Contexto completo del proyecto (infraestructura, esquema, decisiones ya tomadas) en el documento de traspaso que se usó para construir esto.
Arrancar en local
npm install
npm run dev
Sin DATABASE_URL definida, el sitio usa datos de ejemplo
(src/lib/fixtures.ts) con la misma forma que las tablas reales — sirve
para maquetar sin acceso al servidor privado de Hetzner.
Build de producción
DATABASE_URL="postgres://usuario:pass@host-privado:5432/dbname" npm run build
El resultado (dist/) es estático: se sirve con Caddy, sin necesidad de
Node corriendo.
Variables de entorno
| Variable | Uso |
|---|---|
DATABASE_URL |
Conexión a Postgres, solo se usa durante npm run build |
Como el build corre en la misma red privada de Hetzner (vía Coolify), la conexión no necesita salir a internet.
Estructura
src/lib/db.ts— única consulta a Postgres, una vez por build.src/lib/types.ts— forma unificada de un "aviso" (BOIB o prensa).src/components/EntryCard.astro— tarjeta de un aviso (siempre estática).src/components/Filters.astro— filtro por municipio/tema/fuente, JS vanilla que filtra el DOM ya renderizado (sin re-fetch).src/components/MapView.tsx— mapa Leaflet, componente Preact que solo se hidrata en escritorio (client:media="(min-width: 900px)"). En móvil ni Leaflet ni Preact se descargan.src/pages/huella.astro— página de huella (pendiente de rellenar con medición real tras el primer despliegue).
Pendiente para conectar el rebuild automático
Añadir un nodo HTTP Request al final del workflow de n8n de clasificación (tras cada lote) que llame al webhook de deploy de Coolify para esta app, para que el sitio se regenere solo sin intervención manual. La URL del webhook la genera Coolify al crear la aplicación (Settings → Webhooks).
Pendiente de datos
- Unificar
'Ibiza'→'Eivissa'ennoticias_prensa.municipioya está resuelto en la query (case when municipio = 'Ibiza' then 'Eivissa' ...), pero conviene aplicar también elUPDATEen la tabla para no depender de ese parche en cada query. - Sumar Diario de Ibiza como fuente cuando estén confirmadas las URLs RSS.
- Las coordenadas de
municipioCoords(enfixtures.ts, reutilizadas por el mapa) son aproximadas por municipio — no hay geocodificación por entrada individual, ni falta: el mapa agrupa, no geolocaliza cada aviso.