Skip to content

Repository files navigation

Reus Refugi — Worker d'automatització

Cloudflare Worker que automatitza la paperassa de regularització administrativa per a Reus Refugi, una entitat sense ànim de lucre que acompanya persones migrades a regularitzar la seva situació a Espanya.

Estat: en producció des d'abril 2026. L'usen voluntaris no-tècnics diàriament. Aquest repo està obert per transparència i com a referència per a altres entitats del Tercer Sector que vulguin replicar fluxos similars.


Com funciona el flux actual

Tota la paperassa de regularització passa per quatre baules connectades:

  • Tally captura les dades inicials del sol·licitant en una primera trobada, sense haver d'exposar Venus (la base d'Airtable) a persones sense preparació tècnica ni accés operatiu. El formulari de Tally escriu directament a Airtable.
image
  • Venus (Airtable) actua com a core de dades. És la font de veritat única: cada cas, document, factor de vulnerabilitat i relació familiar viu aquí.
  • Venus genera l'informe de vulnerabilitat amb un clic i, a la mateixa fila, prepara un esborrany de Gmail amb el PDF adjunt llest per enviar al usuari.
image
  • Un userscript de Tampermonkey automatitza la pujada de dades de Venus cap a Mercurio, omplint els ~144 camps del formulari telemàtic EX-31/EX-32
    • pujada de arxius perquè el voluntari només hagi de revisar i signar amb AutoFirma.
Tally  →  Venus (Airtable)  ─┬─→  Informe de vulnerabilitat (PDF) + draft Gmail
                             ├─→  Dossier EX-31 / EX-32 (PDF)
                             └─→  Mercurio (auto-fill via Tampermonkey)
image

🩺 Omplir informes de vulnerabilitat — en producció

POST /anexo2 — el voluntari prem un botó a la taula Informes de Vulnerabilitat d'Airtable i, en pocs segons, es genera un certificat de vulnerabilitat oficial (Anexo II del procediment EX-32) signat amb les dades de l'entitat acreditada (RECEX), llest per adjuntar a la sol·licitud i per enviar com a esborrany de Gmail amb un altre clic.

Detall tècnic: el template és el PDF buit oficial del Ministeri (descarregat d'inclusion.gob.es) sense AcroForm; pintem totes les dades — incloent el segell i la firma del representant — a coordenades absolutes a runtime. El PDF lliurat no és editable i renderitza idèntic a tot arreu (Adobe, Chrome, Drive, Samsung Notes…). Vegeu src/anexo2.ts i src/anexo2-coords.ts.

🤖 Omplir Mercurio automàticament — beta

Mercurio és la plataforma oficial per presentar EX-31 i EX-32 telemàticament: ~144 camps de formulari que els voluntaris havien de copiar-pegar manualment des d'Airtable cas per cas.

Un userscript de Tampermonkey servit per GET /mercurio.user.js:

  1. Detecta quan ets a la pantalla d'EX-31 o EX-32 a Mercurio.
  2. Mostra un panell flotant amb cercador de casos d'Airtable Venus.
  3. Quan cliques un cas, omple els 144 camps automàticament — incloent les cascades AJAX (província → municipi → localitat), radio buttons dinàmics, checkboxes condicionals i el bloc reagrupant (DA 21ª) si el cas té un referent familiar.
  4. No submiteja. El voluntari revisa les dades i prem "Firmar y registrar" amb AutoFirma.

És beta: codis confirmats per a DA 21ª Laboral/Familiar/Vulnerabilitat; codis de DA 20ª (PI) encara s'estan validant amb casos reals. La pujada de documents adjunts (passaport, antecedents, etc.) està implementada.

Endpoints relacionats:

  • GET /mercurio.user.js — userscript autoinstal·lable (auto-update diari).
  • GET /mercurio/cases?q=text — cerca casos a Airtable.
  • GET /mercurio/payload?caso=recXXX — retorna els 144 camps mapats per a un cas.

Codi a src/mercurio/:

  • mapping.ts — Airtable case → payload Mercurio (143 camps + lògica DA 20ª/DA 21ª).
  • catalogs.ts — codis estàtics del DOM Mercurio (sexe, província, parentesco…).
  • userscriptCode.ts — template del userscript Tampermonkey.

Altres endpoints (accessoris)

També hi ha codi al Worker per a aquestes coses, però no són el focus del projecte ara mateix. Els documentem aquí perquè existeixen i funcionen:

  • POST /gmail-draft — proxy a Google Apps Script. Crea un esborrany a Gmail amb el PDF adjunt. Existeix perquè GAS retorna 302 que el navegador no pot seguir; aquest endpoint fa la crida server-side.

Captura de dades inicial — Tally → Airtable

Les dades inicials del cas (la primera trobada amb el sol·licitant) es capturen amb un formulari de Tally que els voluntaris fan servir com a portafoli d'entrada. El formulari escriu directament a la base Venus d'Airtable via la integració nativa Tally → Airtable.

A partir d'aquí, els voluntaris enriqueixen el cas dins d'Airtable (documents, situació familiar, factors de vulnerabilitat, etc.) i el Worker treballa sobre aquestes dades.


Arquitectura

┌──────────────────┐  ┌──────────────────┐  ┌────────────────────────┐
│  Tally (entrada) │  │ Airtable (Venus) │  │ Mercurio (gov.es)      │
└────────┬─────────┘  └────────┬─────────┘  └──────────┬─────────────┘
         │  webhook              │  REST API           │ userscript injecta DOM
         └────────────►──────────┘                     │
                                 │                     │
                       ┌─────────▼─────────────────────▼──────────┐
                       │   Cloudflare Worker (aquest repo)        │
                       │   - /generate     dossier EX-31/EX-32     │
                       │   - /anexo2       informe vulnerabilitat  │
                       │   - /mercurio/*   cerca + payload + JS    │
                       │   - /gmail-draft  proxy a GAS             │
                       │   secrets: AIRTABLE_TOKEN, SHARED_SECRET  │
                       │   assets: plantilles PDF oficials         │
                       └──────────────────────────────────────────┘

Setup des de zero

Prereqs

  • Node.js 20+
  • Compte de Cloudflare (el pla gratuït cobre el volum d'una entitat petita).
  • Personal Access Token d'Airtable amb scopes data.records:read i data.records:write limitats a la teva base.

Passos

# 1. Clona i instal·la
git clone https://github.com/andratwiro/reus-refugi-pdf-worker
cd reus-refugi-pdf-worker
npm install

# 2. Login a Cloudflare (obre el navegador)
npx wrangler login

# 3. Defineix els secrets bàsics
npx wrangler secret put AIRTABLE_TOKEN     # Personal Access Token
npx wrangler secret put SHARED_SECRET      # openssl rand -hex 32

# 4. Defineix el presentador (representant acreditat de la teva entitat)
#    — només cal si vols usar la integració Mercurio
npx wrangler secret put PRESENTADOR_NOMBRE   # "COGNOM1 COGNOM2 NOM" majúscules
npx wrangler secret put PRESENTADOR_NIE
npx wrangler secret put PRESENTADOR_TIPODOC  # NF (NIE) | NV (DNI) | PA (passaport)
npx wrangler secret put PRESENTADOR_MOBIL
npx wrangler secret put PRESENTADOR_EMAIL

# 5. Defineix les dades PII del representant legal de l'entitat
#    — necessari per omplir les seccions 2/3 dels EX-31/EX-32
npx wrangler secret put REPRESENTANT_NOM     # "NOM COGNOM1 COGNOM2" majúscules
npx wrangler secret put REPRESENTANT_DNI     # DNI/NIE del representant legal
npx wrangler secret put REPRESENTANT_TITOL   # ex. "PRESIDENTE", "SECRETARIO"

# 6. Defineix els secrets de Gmail (opcional, només si uses /gmail-draft)
npx wrangler secret put GAS_WEBAPP_URL
npx wrangler secret put GAS_SHARED_SECRET

# 7. Edita wrangler.toml amb els IDs de la teva base d'Airtable
# (AIRTABLE_BASE_ID, CASOS_TABLE_ID, INFORMES_VULN_TABLE_ID, etc.)

# 8. Edita ENTITAT_REUS_REFUGI_BASE a src/mappings.ts amb les dades
# públiques de la teva entitat (nom, NIF, adreça, RECEX, tipusEntitat).

# 9. Posa segell + firma del representant a src/private/ i puja'ls a KV
#    (gitignored — no han d'estar mai al repo públic; vegeu src/private/README.md)
cp /ruta/al/teu/segell.png    src/private/entity-stamp.png
cp /ruta/a/la/teva/firma.png  src/private/representative-signature.png
npx wrangler kv namespace create PRIVATE_BINARIES
# (descomenta el bloc [[kv_namespaces]] de wrangler.toml amb l'ID retornat)
npx wrangler kv key put --binding PRIVATE_BINARIES "entity-stamp" \
    --path src/private/entity-stamp.png
npx wrangler kv key put --binding PRIVATE_BINARIES "representative-signature" \
    --path src/private/representative-signature.png

# 10. Push a main → Cloudflare Workers Builds desplega automàticament.
git push origin main

Comprovar que funciona

curl https://<el-teu-worker>.workers.dev/
# → {"ok":true,"service":"reus-refugi-pdf-worker"}

curl https://<el-teu-worker>.workers.dev/mercurio.user.js | head -30
# → ha de retornar el userscript de Tampermonkey amb @match a Mercurio

Connectar Airtable

Per a /generate: crea un camp Button a la taula Casos que dispari una Automation tipus "Run script". Enganxa el codi de airtable-automation.js i actualitza les dues primeres línies amb el teu WORKER_URL i SHARED_SECRET.

Per a /anexo2: a una Dashboard d'Airtable afegeix una Scripting Extension i enganxa el codi de airtable-extension-anexo2.js (mateixes dues primeres constants). El voluntari selecciona la fila de Informes de Vulnerabilitat i clica Run.

Per a Mercurio: instal·la Tampermonkey al navegador, obre la URL https://<el-teu-worker>.workers.dev/mercurio.user.js i Tampermonkey detectarà el script automàticament.


Estructura del repo

src/
  index.ts             ← router principal (totes les rutes HTTP)
  airtable.ts          ← client Airtable (read, listRecords, uploadAttachment)
  mappings.ts          ← IDs de camps Airtable + dades públiques d'entitat
  fillPdf.ts           ← omplir dossier EX-31/EX-32 (decision tree per Via legal)
  anexo2.ts            ← omplir Informe de Vulnerabilitat (paint a coords absolutes)
  anexo2-coords.ts     ← coordenades hardcoded dels camps de l'Annex II
  mercurio/
    mapping.ts         ← Airtable case → 144 camps Mercurio
    catalogs.ts        ← codis DOM (sexe, província, parentesco…)
    userscriptCode.ts  ← template del userscript Tampermonkey
  private/             ← gitignored — segell + firma de l'entitat (vegeu README intern)

assets/                ← públicament accessible via env.ASSETS.fetch()
  EX31_oficial.pdf, EX31_seccion5.pdf
  EX32_oficial.pdf, EX32_seccion5.pdf
  A2_certificado_vulnerabilidad.pdf   ← versió oficial buida del Ministeri

scripts/
  extract-anexo2-coords.ts ← regenera anexo2-coords.ts si canvies de template
  optimize-airtable-pdfs.ts

mock/                  ← fixtures de casos reals + sintètics per a tests
airtable-automation.js       ← script per Automation /generate (botó Casos)
airtable-extension-anexo2.js ← script per Scripting Extension /anexo2 (Dashboard)
wrangler.toml          ← config del Worker (vars públiques, assets binding, data rules)

Notes per a entitats que vulguin replicar

Plantilla pública d'Airtable

L'esquema de Venus està publicat com a plantilla pública a Airtable Universe:

🔗 Venus — Plantilla regularització (RD 1155/2024)

Pots clonar-la al teu workspace amb un clic — t'estalvies recrear taules, camps, vistes i automatitzacions des de zero. Després només cal:

  1. Connectar el formulari de Tally (o el teu propi punt d'entrada) a la taula Casos.
  2. Substituir els IDs de base i taula a wrangler.toml pels de la teva còpia.
  3. Configurar els secrets de l'entitat (PRESENTADOR_*, REPRESENTANT_*).

Hard-codings específics

  • El presentador (representant acreditat que signa les sol·licituds via Mercurio) es configura via els 5 secrets PRESENTADOR_*. Cada entitat ha de fer-ho amb les seves pròpies dades — no hi ha valors per defecte al codi.

  • El representant legal de l'entitat (qui signa els PDFs EX-31/EX-32) es configura via els 3 secrets REPRESENTANT_*. Cap valor PII queda al codi.

  • Les dades públiques de l'entitat (nom, NIF, domicili, telèfon i email del registre d'associacions, RECEX, tipusEntitat) viuen a ENTITAT_REUS_REFUGI_BASE a src/mappings.ts — cada fork ha d'editar aquesta constant amb les dades de la seva entitat. Posa tipusEntitat: "admin_publica" si ets una administració pública competent en assistència social, o "tercer_sector" si ets una entitat del Tercer Sector inscrita al RECEX.

  • El segell de l'entitat i la firma manuscrita del representant que apareixen a la zona de signatura de l'Annex II viuen a un KV namespace privat de Cloudflare (no al repo, no al binding ASSETS). El directori src/private/ és la font local dels PNGs (gitignored) i només s'utilitza per pujar els bytes a KV un sol cop amb wrangler kv key put. El worker els llegeix a runtime via el binding PRIVATE_BINARIES. Vegeu src/private/README.md per al setup pas a pas.

    🔒 Per què KV i no assets/ o un binding [[rules]] type="Data"?

    1. assets/ s'exposa a la URL pública del worker — qualsevol amb la URL podria descarregar el segell i estampar-lo a documents falsos.
    2. [[rules]] type="Data" empotra els fitxers al bundle del worker però requereix que estiguin al disc al moment del wrangler deploy o el build de Cloudflare Workers Builds. Com que els PNGs són gitignored, el clon de CF Builds no els tindria → build fail. KV té els bytes al servei privat Cloudflare i el worker els llegeix només via el binding — mai exposats per cap URL.
  • wrangler.toml apunta a la base d'Airtable Venus de Reus Refugi (appWuXncpGWaFTR4M). Cada entitat té la seva pròpia base; cal canviar tots els IDs.

  • L'esquema d'Airtable reflecteix el flux operatiu de Reus Refugi i s'ha anat construint segons les necessitats dels voluntaris. La plantilla pública és un punt de partida raonable, però potser voldràs adaptar-la.

Si treballes en una entitat similar i vols adaptar això, obre un issue — mirarem d'extreure les peces reutilitzables (sobretot mercurio/catalogs.ts, que conté codis del DOM Mercurio que són universals).


Notes de seguretat

  • Els secrets viuen només a Cloudflare; mai al repo.
  • /generate, /anexo2, /gmail-draft, /mercurio/cases i /mercurio/payload exigeixen Authorization: Bearer <SHARED_SECRET>.
  • GET /mercurio.user.js és obert (qualsevol pot baixar el userscript). El SHARED_SECRET queda embedded al JS servit; el threat model assumeix que l'entitat acreditada és de confiança i que Cloudflare té audit logs si calgués investigar abús.
  • L'Airtable token ha de tenir scope limitat a la base (no a totes les bases).
  • No es loguegen dades personals — només IDs d'Airtable i mètriques.

Llicència

MIT. Pots forkar, modificar i redistribuir lliurement — inclòs ús comercial — sempre que mantinguis l'avís de copyright. Sense garanties.


Crèdits

Construït per Reus Refugi amb assistència de Claude Code. Si trobes alguna cosa útil aquí o vols col·laborar, escriu a regularitzacio@reusrefugi.cat.

About

reus-refugi-pdf-worker

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages