No description
  • TypeScript 46.5%
  • JavaScript 42.4%
  • HTML 7%
  • CSS 2.4%
  • Java 0.9%
  • Other 0.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
carmine 61b542f6a0
All checks were successful
Android / android (push) Successful in 1m45s
Build e deploy / build (push) Successful in 36s
SSO: entra nell'account con la stessa email, altrimenti lo crea
- l'SSO vince: se esiste già un account con la stessa email (anche creato
  con la password) si entra in quello, che viene collegato all'SSO
- nuovo utente anche se il provider dichiara l'email non verificata
  (Authentik di default manda email_verified=false); restano invito o
  REGISTRATION=sso/open per creare account nuovi
- README ed .env.example aggiornati

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 15:08:07 +01:00
.forgejo/workflows fix sito e app 2 2026-10-25 09:21:04 +01:00
backend SSO: entra nell'account con la stessa email, altrimenti lo crea 2026-10-31 15:08:07 +01:00
docs/screenshots first commit 2026-10-24 11:40:53 +02:00
fdroid fix sito e app 2 2026-10-25 09:21:04 +01:00
frontend SSO: cookie di sessione più piccolo e buffer di nginx più ampi su /callback 2026-10-31 14:57:50 +01:00
scripts fix sito e app 2 2026-10-25 09:21:04 +01:00
site Server MCP: collegamento OAuth per ChatGPT e claude.ai 2026-10-28 12:01:33 +01:00
.env.example SSO: entra nell'account con la stessa email, altrimenti lo crea 2026-10-31 15:08:07 +01:00
.gitignore fix email 2026-10-25 08:12:07 +01:00
docker-compose.registry.yml App Android: accesso SSO e passkey 2026-10-27 14:40:46 +01:00
docker-compose.yml App Android: accesso SSO e passkey 2026-10-27 14:40:46 +01:00
README.md SSO: entra nell'account con la stessa email, altrimenti lo crea 2026-10-31 15:08:07 +01:00

FinaFlow

Le tue finanze personali, sul tuo server.

Conti, movimenti, pagamenti ricorrenti, budget mensile sicuro e proiezione del saldo: un'app web self-hosted che ti dice quanto puoi spendere senza mai restare scoperto.

Panoramica di FinaFlow


Perché FinaFlow

La maggior parte delle app di budget guarda al passato. FinaFlow guarda avanti: conosce le tue entrate e uscite ricorrenti (stipendio, affitto, bollette, rate) e calcola quanto puoi spendere liberamente ogni mese perché il conto principale non vada mai sotto zero nei prossimi 24 mesi, anche se lo stipendio arriva in ritardo.

I dati restano tuoi: gira con Docker su un tuo server, un NAS o un VPS, senza servizi esterni.

Funzionalità

Soldi e conti

  • Conti di ogni tipo (corrente, deposito, contanti, carta, investimenti), con nota libera e saldo calcolato in tempo reale.
  • Totale utilizzabile e totale di tutti i conti, più totali per tipo.
  • Risparmi, obiettivi con cifra da raggiungere (i versamenti si fermano a obiettivo raggiunto) e depositi esclusi dal totale, con importo nascondibile.
  • Conti remunerati: tasso annuo lordo, ritenuta e saldo massimo. Gli interessi maturano ogni giorno e vengono accreditati in automatico (mensile, trimestrale o annuale) al netto; la proiezione ne tiene conto.
  • Trasferimenti tra conti.

Movimenti

  • Entrate e uscite con categorie e sottocategorie.
  • Filtri per mese, intervallo di date, tipo, conto, categoria e sottocategoria.
  • Ricerca libera su nome, categoria, sottocategoria e note.
  • Movimento veloce dalla Panoramica o dal pulsante «+» su telefono.
  • Export CSV per mese o intervallo di date.

Ricorrenti e finanziamenti

  • Pagamenti ricorrenti giornalieri, settimanali, mensili o annuali, con intervallo («ogni 3 mesi») e data di fine.
  • Numero di rate → la data di fine si calcola da sola. Le ricorrenze con una fine diventano finanziamenti, con debito residuo e avanzamento.
  • Pagato / È arrivato registra il movimento e passa alla scadenza successiva.
  • Ritardo massimo dello stipendio (es. «fino a +3 giorni»), di cui budget e proiezioni tengono conto.

Budget e proiezioni

  • Budget libero mensile sicuro, ricalcolato mese per mese su 24 mesi.
  • Spendi il X%: metti da parte ogni mese una riserva per gli imprevisti.
  • Budget per categoria con barra di avanzamento.
  • Proiezione di cassa giorno per giorno dei conti inclusi nel totale, come grafico o tabella mese per mese (saldo a fine mese e minimo), con o senza il budget libero speso.
  • Dove vanno i miei soldi: diagramma di flusso dalle entrate alle spese per categoria e a quanto risparmi, per mese o per anno.
  • Categorie per anno: totali di ogni categoria e sottocategoria, anno per anno.

Prestiti

  • Soldi prestati a qualcuno, con restituzioni parziali e quanto deve ancora rientrare.

Utenti e sicurezza

  • Il primo utente diventa amministratore. Gli altri entrano su invito (con password o SSO) oppure si registrano da soli: con REGISTRATION=sso solo tramite SSO, con REGISTRATION=open anche con email e password (confermando l'email). SSO con qualsiasi provider OIDC (Authentik, Google...).
  • Email e password con verifica via email, recupero password (massimo 3 richieste al giorno).
  • Verifica in due passaggi: con l'app di autenticazione (TOTP, con codici di recupero) oppure, per chi non la usa, con un codice via email a ogni accesso con password (serve l'SMTP configurato); passkey; «esci dagli altri dispositivi».
  • Password salvate con scrypt (parametri OWASP), segreti 2FA e password SMTP cifrati nel database, limiti ai tentativi per IP e per account, intestazioni di sicurezza (CSP, anti-iframe).
  • Ogni utente vede solo i propri dati: l'amministratore gestisce utenti e inviti, mai i conti degli altri.
  • Backup completo in JSON (export e import) ed eliminazione dell'account con tutti i dati.

Assistente AI (MCP)

  • Server MCP integrato: collega Claude (o un altro assistente compatibile) ai tuoi dati e chiedi «quanto ho speso al bar a settembre?» o «segna 12 € di spesa di oggi sul conto corrente».
  • Token personali da Profilo e sicurezza, in sola lettura oppure lettura + movimenti: l'assistente può aggiungere movimenti e modificare o eliminare solo quelli creati da lui; conti, categorie, budget e ricorrenze non si toccano. Vedi Server MCP per l'assistente AI.

Interfaccia

  • PWA: da Chrome, Edge o Safari «Installa app» / «Aggiungi a schermata Home» e FinaFlow si apre a schermo intero come un'app, con icona e scorciatoie (movimento veloce, movimenti, budget). Si aggiorna da sola: quando esce una nuova versione compare «Aggiorna».
  • Tema chiaro e scuro che segue il sistema.
  • Su telefono: barra in basso stile app e tabelle a schede.

Screenshot

Movimenti Budget
Movimenti con filtri, ricerca ed export Budget libero e per categoria
Proiezioni Ricorrenti
Proiezione del saldo per conto Ricorrenti con rate e prossime scadenze
Conti Tema scuro
Conti con totali per tipo Tema scuro

FinaFlow su telefono    Budget su telefono, tema scuro

I dati negli screenshot sono inventati.

Installazione

Servono Docker e Docker Compose. FinaFlow usa tre container: MongoDB, backend Node.js e frontend (nginx).

Con le immagini già pronte

mkdir finaflow && cd finaflow
# scarica docker-compose.registry.yml e .env.example dal repository, poi:
cp .env.example .env
nano .env            # FINAFLOW_IMAGE, APP_URL, SESSION_SECRET (openssl rand -hex 32)...
docker compose -f docker-compose.registry.yml pull
docker compose -f docker-compose.registry.yml up -d

FINAFLOW_IMAGE è il prefisso delle immagini (diventano <prefisso>-backend e <prefisso>-frontend). FINAFLOW_TAG sceglie la versione: stable (ultimo rilascio, il default), latest (ultima build di sviluppo) o un numero di versione.

Compilando dai sorgenti

git clone <url-del-repository> finaflow && cd finaflow
cp .env.example .env
nano .env
docker compose up -d --build

Apri http://localhost:8080 (o la porta scelta con APP_PORT): il primo utente che si registra diventa amministratore.

Su host dietro VPN o exit-node npm può bloccarsi durante la build: per questo il docker-compose.yml usa network: host nella build. Altrove puoi toglierlo.

Aggiornare

docker compose -f docker-compose.registry.yml pull && docker compose -f docker-compose.registry.yml up -d
# oppure, dai sorgenti:
git pull && docker compose up -d --build

All'avvio il backend allinea da solo gli indici del database.

Configurazione

Tutto si imposta nel file .env (vedi .env.example).

Variabile Default Descrizione
APP_URL — URL pubblico dell'app (redirect SSO, link nelle email, passkey)
APP_PORT 8080 Porta pubblicata dal frontend
SESSION_SECRET — Secret per sessioni e dati cifrati: obbligatorio (senza, o con un valore di esempio, il backend non parte), genera con openssl rand -hex 32
TRUST_PROXY 1 Proxy davanti al backend: 1 solo l'nginx del frontend, 2 se c'è anche un reverse proxy (Traefik, NPM, Caddy). Serve per i limiti ai tentativi di accesso per IP
FINAFLOW_IMAGE / FINAFLOW_TAG — / stable Immagini da usare con docker-compose.registry.yml
REGISTRATION invite Chi può creare un account da solo: invite nessuno (solo su invito) · sso solo con l'SSO · open anche con email e password (accesso dopo la conferma dell'email)
SSO_DISABLED false true = solo accesso con email e password
ISSUER_BASE_URL — Issuer OIDC (es. Authentik, termina con /; Google: https://accounts.google.com)
SSO_NAME dal provider Nome sul pulsante «Accedi con …» (default Google, Authentik o SSO)
CLIENT_ID / CLIENT_SECRET — Credenziali del provider OIDC
SCOPE openid profile email Scope OIDC
AUTH_REQUIRED true Lasciare true in produzione
SMTP_HOST / SMTP_PORT / SMTP_SECURE / SMTP_USER / SMTP_PASS / MAIL_FROM — Email di verifica, inviti e recupero password. Senza SMTP i link finiscono nei log del backend
APP_ORIGINS https://localhost,capacitor://localhost Origini dell'app Android ammesse a chiamare le API (CORS): lascia il default
DATA_DIR /data Cartella dei file generati dal backend (assetlinks.json per passkey e link dell'app): montaci un volume
ANDROID_CERT_SHA256 — Impronte SHA-256 di altri certificati di firma dell'APK (separate da virgola), oltre a quella ufficiale
TEST_ENDPOINTS false Solo per gli screenshot automatici dei test: mai in produzione
DEMO_MODE false true = istanza dimostrativa, vedi Demo
TZ Europe/Rome Fuso orario per «oggi», fine mese, scadenze ed export
CURRENCY EUR Valuta per i nuovi utenti
LOW_BALANCE_THRESHOLD 0 Soglia di avviso del saldo
FORECAST_MONTHS 12 Mesi di proiezione di default

Email (SMTP)

Si configura nel .env oppure dal pannello Amministrazione → Email (SMTP). Le impostazioni salvate dall'amministratore (password cifrata nel database) hanno sempre la precedenza sul .env.

Se le email (conferma, inviti, recupero password) non arrivano, prova l'invio dal server: mostra la configurazione in uso e l'errore esatto. Ogni invio riuscito o fallito è anche nei log del backend (righe [mail] e [recupero]).

docker exec -it finaflow-backend node src/cli.js test-mail tuo@indirizzo.it
docker logs finaflow-backend 2>&1 | grep -E '\[mail\]|\[recupero\]'

Il recupero password vale per gli account con email e password: chi accede solo con SSO riceve un'email che lo invita ad accedere con l'SSO.

SSO con Authentik, Google o un altro provider OIDC

  1. In Authentik: Applications → Providers → Create, tipo OAuth2/OpenID Connect, confidential.
  2. Redirect URIs: <APP_URL>/callback e <APP_URL>/login. Post-logout redirect: <APP_URL>.
  3. Crea un'Application collegata al provider.
  4. Nel .env: ISSUER_BASE_URL=https://authentik.tuodominio.it/application/o/<slug>/, CLIENT_ID, CLIENT_SECRET, SSO_DISABLED=false.

Dietro un reverse proxy HTTPS imposta APP_URL=https://...: i cookie sicuri si attivano da soli.

Google: in Google Cloud Console → API e servizi → Credenziali crea un ID client OAuth di tipo Applicazione web con URI di reindirizzamento <APP_URL>/callback, poi nel .env ISSUER_BASE_URL=https://accounts.google.com, CLIENT_ID, CLIENT_SECRET. Il pulsante diventa «Accedi con Google»; all'uscita si esce da FinaFlow ma non dall'account Google.

Con l'SSO vince l'email: se esiste già un account con la stessa email (anche creato con la password) si entra in quello, che da allora è collegato all'SSO. Altrimenti viene creato un nuovo utente, se ha un invito per quell'email oppure REGISTRATION=sso/open. Ci si fida dell'email del provider: usa solo un provider in cui gli utenti non possono scegliersi un'email altrui. Con Google lascia REGISTRATION=invite se non vuoi che entri chiunque abbia un account Google. Per avere più provider insieme (es. Google e account locali Authentik) collega Google come source in Authentik.

Amministratore senza password

Reimposta la password dal server, disattiva la verifica in due passaggi e chiude le sessioni aperte:

docker exec -it finaflow-backend node src/cli.js list-admins
docker exec -it finaflow-backend node src/cli.js reset-password admin@tuodominio.it              # genera una password
docker exec -it finaflow-backend node src/cli.js reset-password admin@tuodominio.it NuovaPass123  # oppure la scegli tu

Backup

Ogni utente può scaricare e reimportare tutti i propri dati da Profilo e sicurezza → I tuoi dati (file JSON). Per un backup dell'intera istanza salva il volume mongo-data, per esempio:

docker exec finaflow-mongo mongodump --archive --db finaflow > finaflow-$(date +%F).archive

Server MCP per l'assistente AI

FinaFlow espone un server MCP su /api/mcp (Streamable HTTP, senza stato). Ogni utente crea i propri token da Profilo e sicurezza → Assistente AI (MCP) (serve la password o il codice 2FA); il token si vede una sola volta e nel database ne resta solo l'hash.

Permessi Strumenti
Sola lettura list_accounts, list_categories, search_transactions, totals_by_category, get_budget, get_forecast, list_recurring
Lettura + movimenti in più add_transaction (uscite, entrate, trasferimenti), update_transaction e delete_transaction solo sui movimenti creati dall'assistente

ChatGPT, claude.ai e le altre app con OAuth non hanno bisogno di token: aggiungi un connettore personalizzato con l'indirizzo https://finaflow.example.com/api/mcp, accedi a FinaFlow (con qualsiasi metodo) e scegli i permessi nella pagina di consenso. Il server implementa OAuth 2.1 come chiede MCP: metadati (/.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server), registrazione dinamica dei client, PKCE S256 obbligatorio, token di accesso di un'ora con refresh token che cambia a ogni rinnovo. Il collegamento compare tra i token in Profilo e sicurezza e si revoca da lì. Il server deve essere raggiungibile in HTTPS da internet (ChatGPT e claude.ai si collegano dai loro server).

Per i client con intestazioni personalizzate, dopo la creazione del token l'app mostra la configurazione già compilata (da copiare o scaricare) per:

  • Claude Code: claude mcp add --scope user --transport http finaflow https://finaflow.example.com/api/mcp --header "Authorization: Bearer ffk_..."
  • Claude Desktop: claude_desktop_config.json con npx mcp-remote (serve Node.js):
    { "mcpServers": { "finaflow": {
        "command": "npx",
        "args": ["-y", "mcp-remote", "https://finaflow.example.com/api/mcp", "--header", "Authorization:${FINAFLOW_AUTH}"],
        "env": { "FINAFLOW_AUTH": "Bearer ffk_..." } } } }
    
  • OpenCode: ~/.config/opencode/opencode.json (o opencode.json nel progetto):
    { "$schema": "https://opencode.ai/config.json",
      "mcp": { "finaflow": { "type": "remote", "url": "https://finaflow.example.com/api/mcp", "enabled": true,
        "headers": { "Authorization": "Bearer ffk_..." } } } }
    

L'endpoint accetta solo token (mai i cookie del browser). Token e collegamenti OAuth smettono di valere quando si cambia la password, quando si usa «Esci dagli altri dispositivi» o quando l'amministratore disattiva l'utente; si revocano uno per uno dalla stessa pagina.

Demo

Con DEMO_MODE=true l'istanza diventa una demo pubblica:

  • all'avvio e ogni notte a mezzanotte cancella tutti i dati e ricrea un utente con conti, movimenti, ricorrenti, obiettivi e prestiti fittizi, con date sempre aggiornate;
  • si entra con demo@demo.it / demo1234 (la pagina di accesso lo mostra, con il pulsante «Entra nella demo»);
  • registrazione, recupero password, cambio password, 2FA, passkey, eliminazione dell'account, amministrazione e import del backup sono disattivati; l'SSO è spento.

Usala solo su un'istanza dedicata, con un suo database: non attivarla dove ci sono dati veri.

App Android e repository F-Droid

L'app Android (Capacitor, cartella frontend/android) contiene l'interfaccia e si collega al tuo server: al primo avvio chiede l'indirizzo dell'istanza. Funzionano accesso con email e password, verifica in due passaggi, SSO, passkey e tutte le pagine. Export CSV, backup e codici di recupero si salvano tramite la condivisione di Android.

  • SSO: il login si apre nel browser di sistema e torna all'app da solo (link it.devmin.finaflow://sso con un codice monouso, protetto come PKCE). In Authentik non va cambiato nulla.
  • Passkey (Android 14+): native, con Credential Manager; quelle create dal browser valgono anche nell'app e viceversa. Il dominio deve pubblicare /.well-known/assetlinks.json con l'impronta dell'APK: lo genera il backend a ogni avvio in DATA_DIR (volume backend-data nei compose), già con l'impronta dell'APK ufficiale. Se firmi un tuo APK aggiungi la sua impronta in ANDROID_CERT_SHA256 (o direttamente nel file nel volume: le aggiunte restano). Il server deve essere raggiungibile in HTTPS da internet, perché Google verifica il file.

Il workflow .forgejo/workflows/android.yml parte con un tag v1.2.3 (o a mano): compila l'APK firmato e lo allega alla release v1.2.3 di Forgejo, da cui si scarica e si installa.

Facoltativo: impostando la variabile FDROID_REPO_URL crea anche un repository F-Droid firmato e lo pubblica come immagine finaflow-fdroid (nginx, da mettere dietro il tuo reverse proxy, es. https://fdroid.tuodominio.it): la sua pagina spiega come aggiungerlo a F-Droid (pulsante, indirizzo con impronta e QR code) e da lì gli aggiornamenti arrivano in automatico.

Preparazione (una volta sola) — crea il keystore e conservalo con cura: con un'altra chiave gli aggiornamenti non si installano più.

keytool -genkeypair -keystore finaflow.jks -alias finaflow -keyalg RSA -keysize 4096 -validity 10000 -dname "CN=FinaFlow"
base64 -w0 finaflow.jks   # -> secret ANDROID_KEYSTORE_BASE64
Tipo Nome Valore
Variabile FDROID_REPO_URL facoltativa: indirizzo pubblico del repository F-Droid, es. https://fdroid.tuodominio.it/repo
Secret ANDROID_KEYSTORE_BASE64 / ANDROID_KEYSTORE_PASSWORD / ANDROID_KEY_ALIAS keystore in base64, password, alias
Secret ANDROID_KEY_PASSWORD password della chiave, se diversa (facoltativo)
Secret RELEASE_TOKEN token Forgejo per le release (facoltativo: di default usa il token automatico del workflow)
Secret DEPLOY_WEBHOOK_FDROID webhook per aggiornare il container del repository (facoltativo)

Compilare l'APK in locale (serve Docker):

docker run --rm -v "$PWD":/src -w /src node:24-trixie bash scripts/build-apk.sh 1.2.3   # -> dist-apk/

Build automatica con Forgejo Actions

Il workflow .forgejo/workflows/build.yml compila le immagini finaflow-backend, finaflow-frontend e finaflow-site (il sito vetrina, pubblicato solo nel registry), le pubblica e chiama un webhook per aggiornare il server (Portainer, Komodo, Watchtower o uno script tuo).

Evento Tag delle immagini Webhook
push su dev latest DEPLOY_WEBHOOK_DEV
tag v1.2.3 (es. creato su main) 1.2.3, stable, latest DEPLOY_WEBHOOK_STABLE
push su main nessuna build: si rilascia creando il tag —

Variabili (facoltative): REGISTRY (default: l'host di Forgejo), IMAGE_OWNER, IMAGE_NAME (default finaflow), PLATFORMS (default linux/amd64).

Secret: REGISTRY_USERNAME, REGISTRY_TOKEN (token con permesso write:package) e, facoltativi, DEPLOY_WEBHOOK_STABLE (rilasci), DEPLOY_WEBHOOK_DEV, DEPLOY_WEBHOOK_TOKEN (inviato come Authorization: Bearer).

Il runner deve avere l'etichetta docker e accesso a Docker (container.docker_host: "automount" nel config.yml di forgejo-runner).

Sviluppo

docker run -d -p 27017:27017 --name mongo-dev mongo:9

cd backend && npm install && npm run dev      # http://localhost:3000
cd frontend && npm install && npm start       # http://localhost:4200 (proxy verso :3000)

Test

cd backend
npm test                                                        # test unitari
MONGO_URI_TEST=mongodb://localhost:27017/finaflow_test npm test # + test delle API (il database viene svuotato)

I test unitari coprono ricorrenze e rate, budget libero (spendendolo tutto il conto non va mai sotto zero), interessi, password, 2FA e cifratura. Quelli di integrazione provano le API su un database vero: accesso e inviti, conti e trasferimenti, ricerca e filtri, finanziamenti, prestiti, interessi, isolamento tra utenti, cambio password, backup, eliminazione dell'account e demo.

Sito vetrina

La cartella site/ è una pagina statica con il suo Dockerfile (nginx): la pipeline la pubblica come immagine finaflow-site, non fa parte dei docker compose dell'app.

docker run -d -p 8081:80 <registry>/<utente>/finaflow-site:latest

Stack: Angular 22 (componenti standalone, signals, build con esbuild) e Tailwind CSS · Node.js 24 con Express e Mongoose · MongoDB 9 · Chart.js.

finaflow/
├── docker-compose.yml            # build dai sorgenti
├── docker-compose.registry.yml   # immagini dal registry
├── .forgejo/workflows/build.yml  # CI: build, push, webhook
├── backend/
│   ├── tests/                    # unit/ e integration/ (node --test)
│   └── src/
│       ├── app.js                # app Express: sessioni, SSO, API
│       ├── server.js             # avvio: database, demo, interessi
│       ├── cli.js                # reset password admin
│       ├── models/               # utenti, conti, movimenti, ricorrenti, budget...
│       ├── routes/               # API dati, auth, sicurezza, admin
│       └── services/             # saldi, ricorrenze, budget e proiezioni, interessi, backup, email, 2FA
├── frontend/src/app/
│   ├── pages/                    # panoramica, movimenti, ricorrenti, budget, proiezioni, prestiti...
│   └── core/                     # servizi, guardie, componenti condivisi
├── docs/screenshots/
└── site/                         # sito vetrina statico (immagine finaflow-site)