fasas-deploy

Versione cloud: il FASAS su un dominio, con Supabase

Il FASAS gira in un solo container presso un servizio di hosting (esempio pronto: Render, file render.yaml), risponde al dominio della struttura (es. https://fasas.miacooperativa.it) e tiene dati e file su Supabase (database PostgreSQL e Storage). Nessun server in struttura, niente Redis né proxy da configurare.

browser / telefoni ──HTTPS──▶ dominio ──▶ servizio di hosting: container FASAS ──▶ Supabase (UE)
                                          · sito (gunicorn, file statici)          · PostgreSQL: dati
                                          · pianificatore dei job                  · Storage: file cifrati
                                                                                    · (bucket dei backup)

Per l’installazione su un PC in struttura si usa soltanto la repository originale, non questa versione cloud.

Cosa fa il FASAS da solo in modalità cloud (FASAS_CLOUD=1)

Costi (da decidere prima: nessun servizio a pagamento è attivato da questa configurazione)

Servizio Piano gratuito Piano a pagamento
Supabase progetto messo in pausa dopo 7 giorni di scarsa attività; nessun backup giornaliero di Supabase (resta quello del FASAS, passo 5) Pro: mai in pausa, backup giornalieri di Supabase per 7 giorni
Render il servizio si addormenta dopo un periodo senza visite: la prima pagina arriva in circa un minuto e i job girano solo da sveglio (al risveglio recupera quelli saltati) Starter o superiore: sempre acceso
Dominio quello che la struttura ha già —
Copia fuori sede con blocco delle modifiche bucket Supabase (incluso, senza blocco) es. Backblaze B2 con Object Lock (gratuito fino a una soglia di spazio)

render.yaml usa il piano free. Per l’uso reale con gli ospiti servono i piani sempre accesi: è una scelta della struttura e si fa dal pannello di Render e di Supabase (prezzi correnti sui loro siti).

Passo 1 – Chiavi (sul proprio PC)

Nella cartella del FASAS:

python manage.py fasas_genkey

Senza il FASAS installato basta Python:

python -c "import base64,os,secrets; print('DJANGO_SECRET_KEY=' + secrets.token_urlsafe(50)); print('FASAS_ENCRYPTION_KEYS=' + base64.urlsafe_b64encode(os.urandom(32)).decode()); print('FASAS_BACKUP_KEY=' + base64.urlsafe_b64encode(os.urandom(32)).decode())"

FASAS_ENCRYPTION_KEYS e FASAS_BACKUP_KEY vanno stampate e chiuse nella busta di custodia (SICUREZZA.md): senza di esse documenti e backup non si leggono più, nemmeno con l’accesso a Supabase.

Passo 2 – Supabase: progetto e database

  1. Nuovo progetto, regione UE (consigliata Francoforte, eu-central-1, la stessa di render.yaml). Annotare la password del database.
  2. Applicare le migrazioni della cartella supabase/migrations, nell’ordine:
    • …_fasas_schema_e_dati_iniziali.sql: tabelle, vincoli, protezioni dell’audit, ruoli e permessi, configurazione iniziale (nessun dato personale, nessun utente);
    • …_fasas_protezione_api.sql: RLS su tutte le tabelle e nessun privilegio ai ruoli delle API di Supabase.

    Tre modi equivalenti: SQL Editor › New query › incollare il file › Run (poi il secondo); oppure psql con la stringa del punto 4:

    psql "postgresql://postgres.RIFERIMENTO:PASSWORD@aws-0-eu-central-1.pooler.supabase.com:5432/postgres" -v ON_ERROR_STOP=1 --single-transaction -f supabase/migrations/20261106120000_fasas_schema_e_dati_iniziali.sql
    

    oppure la CLI di Supabase (supabase init, supabase link sul progetto, poi supabase db push). Se si salta questo passo il FASAS crea comunque tutto al primo avvio: le migrazioni servono a preparare e controllare il database prima.

  3. API automatiche di Supabase: il FASAS non le usa. Nelle impostazioni del progetto, sezione Data API, disattivarle (o togliere public dagli schemi esposti). Anche lasciandole attive le tabelle restano chiuse (RLS senza policy e nessun privilegio), ma è meglio non esporle affatto.
  4. Stringa di connessione: pulsante Connect › Session pooler (porta 5432), sostituire la password. Non usare «Direct connection» (solo IPv6, molti servizi di hosting non la raggiungono) né il «Transaction pooler» (porta 6543: il backup con pg_dump non funziona).
  5. Mai usare o copiare la chiave service_role (o «secret key»): il FASAS non ne ha bisogno.

Passo 3 – Supabase: Storage per i documenti

  1. Storage › New bucket fasas-documenti, non pubblico. Per la copia dei backup (passo 5, scelta A) un secondo bucket non pubblico fasas-backup.
  2. Storage › Settings › S3 Connection: attivare la connessione S3, annotare Endpoint (es. https://RIFERIMENTO.storage.supabase.co/storage/v1/s3) e Region.
  3. New access key: annotare Access key ID e Secret access key (il segreto si vede una volta sola). Sono chiavi solo per il server: non vanno mai in una pagina web o in un’app.

Passo 4 – Render: il servizio

  1. Render › New › Blueprint › repository del FASAS su GitHub: legge render.yaml.
  2. Compilare le variabili richieste (elenco completo e commentato in .env.cloud.example): chiavi del passo 1, FASAS_DOMAIN, DATABASE_URL (passo 2.4), FASAS_S3_* (passo 3), copia fuori sede (passo 5), FASAS_ADMIN_USERNAME e FASAS_ADMIN_PASSWORD (almeno 10 caratteri, non banale).
  3. Avviare. Il primo avvio costruisce l’immagine (qualche minuto). Nel registro deve comparire Amministratore … creato e Pianificatore avviato.
  4. Dominio: Settings › Custom Domains › aggiungere fasas.miacooperativa.it; presso chi gestisce il dominio creare il record DNS indicato da Render (di solito CNAME fasas → NOME.onrender.com). Il certificato HTTPS arriva da solo. FASAS_DOMAIN deve essere proprio quel nome.
  5. Aggiornamenti: render.yaml non pubblica da solo a ogni modifica su GitHub. Dopo il collaudo di una nuova versione: Manual Deploy. Lo schema del database si aggiorna da solo all’avvio: non c’è SQL da eseguire.

Altri servizi che avviano un Dockerfile (Railway, Fly.io, Koyeb, Google Cloud Run…) vanno bene allo stesso modo: stesse variabili, comando predefinito dell’immagine, porta nella variabile PORT, controllo di salute /salute/.

Passo 5 – Copia fuori sede dei backup

Il disco del container è temporaneo: il backup della notte va copiato fuori, altrimenti si perde al primo riavvio.

I backup sono cifrati con FASAS_BACKUP_KEY prima di uscire: il fornitore non li può leggere. Contengono database e file dello Storage (i backup di Supabase, dove ci sono, contengono solo il database).

Passo 6 – Primo accesso e controllo

  1. Aprire https://fasas.miacooperativa.it, entrare con l’amministratore: il FASAS chiede subito la verifica in due passaggi (app sul telefono).
  2. Togliere FASAS_ADMIN_PASSWORD dalle variabili di Render.
  3. Sistema › Pannello di sistema e Errori del sistema: database, file (object storage), backup, audit e job in verde. La mattina dopo: Sistema › Backup mostra il backup delle 02:30 e la copia fuori sede.
  4. Controllo dall’esterno (facoltativo, gratuito): un servizio di monitoraggio che chiama https://fasas.miacooperativa.it/salute/ (MONITORAGGIO.md); il dettaglio solo con FASAS_HEALTH_TOKEN (generato da Render: lo si legge tra le variabili).

Cosa cambia rispetto all’installazione in struttura

Tema In cloud
Disco di continuità, gruppo di continuità, VPN, disco USB mensile Non servono: i rispettivi controlli restano spenti
Internet in struttura Indispensabile. Se manca: modalità senza rete e foglio di emergenza (PIANO_DI_CONTINUITA.md); conviene una seconda linea (anche il telefono)
Accesso da fuori Il FASAS è su internet: verifica in due passaggi obbligatoria per amministratore, coordinatore e medico (consigliata per tutti: FASAS_2FA_REQUIRED_ROLES)
Limitare l’accesso alla rete della struttura Possibile con FASAS_ALLOWED_NETWORKS (IP pubblico della struttura); provarlo prima, perché blocca anche i telefoni fuori dal Wi-Fi
IP degli utenti nel registro degli accessi Letto dall’intestazione del proxy del servizio di hosting (FASAS_TRUSTED_PROXY_COUNT=1 predefinito in cloud). Se nel registro compare sempre lo stesso indirizzo per tutti, il servizio ha più proxy in fila: aumentare il numero
Prova di ripristino trimestrale Parziale (controllo dell’archivio) se non c’è un secondo database vuoto in FASAS_RESTORE_TEST_DATABASE_URL
Ripristino completo Su un progetto Supabase nuovo: python manage.py backup_restore --confirm FILE con le variabili del nuovo progetto; i file tornano nel bucket indicato da FASAS_S3_BUCKET
Firma con CIE Uguale: l’app FASAS Signer si collega al dominio HTTPS

Sicurezza e GDPR

Problemi comuni

Messaggio nel registro Causa
fasas.E102 / fasas.E103 Mancano o sono incomplete le variabili FASAS_S3_*
fasas.E104 Manca FASAS_DOMAIN
fasas.E106 Mancano FASAS_ENCRYPTION_KEYS o FASAS_BACKUP_KEY
password authentication failed Password sbagliata in DATABASE_URL; caratteri come @ : / # vanno scritti come %40 %3A %2F %23
Network is unreachable verso db.….supabase.co Si sta usando «Direct connection»: usare «Session pooler»
Pagina «Bad Request (400)» Il nome nella barra degli indirizzi non è in FASAS_DOMAIN
Errore CSRF (403) dopo l’accesso Si apre il sito con un nome diverso da FASAS_DOMAIN o senza HTTPS
Il caricamento di un documento fallisce Bucket inesistente o chiavi S3 sbagliate (Sistema › Errori del sistema)

Come è stato provato

Per chi sviluppa