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.
FASAS_CLOUD=1)migrate), protegge le tabelle dalle API di Supabase, crea il primo
amministratore dalle variabili se non ce n’è nessuno, poi avvia sito e pianificatore./salute/ risponde anche ai controlli interni del servizio di hosting.| 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).
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.
eu-central-1, la stessa di render.yaml).
Annotare la password del database.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.
public dagli schemi esposti). Anche lasciandole attive le tabelle restano chiuse
(RLS senza policy e nessun privilegio), ma è meglio non esporle affatto.service_role (o «secret key»): il FASAS non ne ha bisogno.fasas-documenti, non pubblico. Per la copia dei backup (passo 5, scelta A) un
secondo bucket non pubblico fasas-backup.https://RIFERIMENTO.storage.supabase.co/storage/v1/s3) e Region.render.yaml..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).Amministratore … creato e Pianificatore avviato.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.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/.
Il disco del container è temporaneo: il backup della notte va copiato fuori, altrimenti si perde al primo riavvio.
fasas-backup (nessun costo in più): FASAS_OFFSITE_ENDPOINT, FASAS_OFFSITE_REGION,
FASAS_OFFSITE_ACCESS_KEY, FASAS_OFFSITE_SECRET_KEY uguali a quelli del passo 3, FASAS_OFFSITE_BUCKET=fasas-backup,
FASAS_OFFSITE_LOCK_DAYS=0. Limite: Supabase Storage non ha il blocco delle modifiche e sta presso lo stesso
fornitore del database.FASAS_OFFSITE_LOCK_DAYS=30. Nessuno, nemmeno con le chiavi, può cancellare le copie
prima della scadenza.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).
https://fasas.miacooperativa.it, entrare con l’amministratore: il FASAS chiede subito la verifica in due
passaggi (app sul telefono).FASAS_ADMIN_PASSWORD dalle variabili di Render.https://fasas.miacooperativa.it/salute/ (MONITORAGGIO.md); il dettaglio solo con
FASAS_HEALTH_TOKEN (generato da Render: lo si legge tra le variabili).| 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 |
anon, authenticated,
service_role); il FASAS lo ricontrolla a ogni avvio.| 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) |
fasas/core/scheduler.py (usati anche da Celery). Avvio: docker/cloud.sh.python scripts/genera_migrazioni_supabase.py postgres://…/database_vuoto (il test
MigrazioniSupabaseTest segnala quando servono).python scripts/prova_cloud.py NUMERO_OSPITE_DI_PROVA (variabili in testa
allo script).