IEM

100 Cims

Codi privat
  • Graella de cims amb foto, nom, comarca i una insígnia per als cims essencials del repte.
    Els 522 cims del repte, amb desplaçament infinit
  • Mode taula: columnes ordenables amb altitud, comarca i coordenades, amb filtre i selector de columnes.
    Mode taula, amb filtre i columnes ordenables
  • Diàleg de detall d'un cim amb les seves dades i enllaços a les rutes de Wikiloc i a Google Maps.
    Detall d'un cim, amb rutes i com arribar-hi

Aplicació sobre el repte dels 100 Cims de la FEEC. Fa servir Angular per consultar els cims, una API REST amb Spring Boot i PostgreSQL, i scripts en Python per recopilar i preparar les dades i les imatges.

  • API REST amb Maven i PostgreSQL 17 en contenidor.
  • Procés en Python que recopila les dades dels cims i les carrega a la base de dades.
  • Descàrrega i optimització de les imatges a WebP.
  • Restauració del conjunt de dades des de CSV.

El problema

El repte 100 Cims de la FEEC són 522 cims, i la llista oficial viu en documents pensats per imprimir-se: no hi ha manera de filtrar-la, ordenar-la ni marcar per on vas. Qui ho intenta acaba amb un full de càlcul a mà que es desactualitza sol. La idea era un catàleg públic dels 522 i, darrere d'una sessió, el progrés de cadascú.

El meu paper

Tot: la interfície en Angular, l'API en Spring Boot, l'esquema de PostgreSQL, la canalització de dades en Python i l'empaquetat en Docker.

Decisions

  1. Les dades les prepara un procés, no una persona

    Set guions de Python recullen els cims, descarreguen les seves imatges, les optimitzen a WebP, les insereixen i poden restaurar el conjunt sencer des del CSV. Teclejar 522 files a mà es fa una vegada; quan la FEEC corregeix una altitud o canvia una foto cal tornar a començar, i llavors el que es cansa reescriu només el que recorda. Amb el procés, refer-ho és tornar a llançar-lo.

  2. La càrrega exigeix exactament 522, o falla

    La migració de Flyway és idempotent i comprova el número. Una càrrega que insereix 500 de 522 i continua endavant deixa l'aplicació dempeus, amb aspecte correcte i amb vint-i-dos cims que no existeixen per a ningú; ningú la mira fins que algú busca el seu i no hi és. Fallar a l'arrencada és sorollós, i això és exactament el que es vol d'una dada que es carrega una vegada.

  3. Les imatges a R2, verificades per hash

    La sincronització amb Cloudflare R2 és idempotent, torna a descarregar cada objecte, compara el seu SHA-256 i exigeix que les claus siguin exactament 522. Un «pujat correctament» que ningú torna a llegir no diu res: el que importa és que el que hi ha a dalt sigui byte a byte el que es va pujar. En desenvolupament les serveix Nginx des de la còpia local, així que aixecar el projecte no depèn del bucket.

  4. Les proves d'integració van contra una base de veritat

    Testcontainers aixeca PostgreSQL 18.4 per a les proves del backend, i si no hi ha Docker se salten en lloc de caure a una base local. Una prova contra una base simulada passa mentre la migració real falla, que és el mode de fallada que de debò importa aquí; i una que fa servir la base del portàtil arrossega les dades d'ahir. Les imatges de Docker van fixades per versió **i** digest, perquè la d'aquí a un any sigui la mateixa.

Resultat

  • Els 522 cims en un catàleg amb filtres, fitxes, fotografies i taula ordenable, que es reconstrueix sencer des del CSV amb una ordre.
  • El progrés personal queda darrere de Google OAuth, amb CSRF per a SPA i galetes de sessió `HttpOnly`, `SameSite=Lax` i `Secure`: el catàleg és públic i el que has pujat tu, no.
  • Tot l'stack arrenca amb un `docker compose up`: PostgreSQL 18, Flyway, l'API en Java 25 i l'Angular servit per Nginx, amb cada imatge fixada per digest.

Què canviaria

Les 522 imatges en WebP van entrar al repositori abans que existís el bucket, i allà continuen: l'historial de Git carrega amb elles i cal purgar-lo amb `filter-repo` tan bon punt el domini de mitjans estigui connectat. Un binari que entra a Git no se'n va sol, i reescriure l'historial d'un repositori compartit és car. Avui començaria per l'emmagatzematge d'objectes encara que al principi semblés desproporcionat per a una carpeta de fotos.

  • Angular
  • TypeScript
  • Java
  • Spring Boot
  • PostgreSQL
  • Python
  • Docker