T ToutadminDocumentazione

Installare l'edizione Node

L'edizione Node gira come un servizio permanente: un processo, una banca dati SQLite sul disco, nient'altro da installare — nessuna banca dati esterna, nessuna cache, nessuna coda. Questa guida va dalla cartella vuota all'istanza in produzione, compreso ciò che la maggior parte delle guide tralascia: il servizio di sistema, il proxy inverso, il TLS, i backup e l'aggiornamento.

Che cosa serve#

ElementoVersionePerché
Node.js22 o più recenteIl prodotto si appoggia al suo esecutore di test integrato e ad API recenti.
Un compilatore Cbuild-essentialbetter-sqlite3 si compila all'installazione, salvo che esista un binario già pronto per la vostra piattaforma.
Un disco localeSQLite e i file depositati. Mai una condivisione di rete: si veda più avanti.

Una macchina a un core con 512 MB di memoria basta per qualche decina di persone. Ciò che conta non è la potenza ma il disco: dev'essere locale e sottoposto a backup.

1. Recuperare e installare#

git clone <il-vostro-deposito> /var/www/toutadmin
cd /var/www/toutadmin
npm ci --omit=dev

npm ci anziché npm install: installa esattamente ciò che descrive il file di blocco, senza mai riscriverlo. Su un server, una versione che scivola è un guasto che nessuno capisce.

Se la compilazione fallisce

better-sqlite3 è l'unica dipendenza che compila del C. sudo apt install -y build-essential python3 risolve la quasi totalità dei casi.

2. Configurare#

cp .env.example .env

Tutto si regola con variabili d'ambiente — lette da .env, o poste dal vostro gestore di servizi. Nessuna è obbligatoria: senza .env, l'istanza parte sulla porta 3000 e vi manda alla procedura guidata.

VariabilePredefinitoChe cosa fa
PORT3000La porta d'ascolto.
NODE_ENVproduction in produzione: cookie severi, nessuna traccia dettagliata.
SESSION_SECRETgeneratoSigilla le sessioni. Lasciato vuoto, viene generato in data/session.key.
INSTALL_TOKENRichiesto dalla procedura guidata prima d'installare. Consigliato su un server esposto.
TRUST_PROXY1 dietro un proxy fidato, e soltanto lì.
DB_PATHdata/app.sqliteLa banca dati.
UPLOAD_DIR, CV_DIR, VAULT_DIR, SIGN_DIR, DOCS_DIR, BACKUP_DIRsotto data/Le cartelle dei file: foto, curriculum, cassaforte, libro firme, documenti ricevuti, archivi.
LOGIN_RATE_LIMIT10Tentativi d'accesso per quarto d'ora e per indirizzo.
GLOBAL_RATE_LIMIT300Richieste al minuto e per indirizzo.
API_RATE_LIMITChiamate d'API al minuto e per token.
SESSION_IDLE_MINUTES60Inattività oltre la quale la sessione cade.
SESSION_MAX_HOURS12Durata assoluta che nessuna attività prolunga.
ADMIN_EMAIL, ADMIN_PASSWORDInstallazione senza interfaccia: crea l'amministratore all'avvio.
Il segreto di sessione

Cambiare SESSION_SECRET disconnette tutti in un colpo. Ponetelo una volta, generatelo a caso (node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"), e salvatelo insieme al resto. Non lasciatelo mai in un deposito git.

Porre un token d'installazione#

Fra il primo avvio e il vostro passaggio nella procedura guidata, l'istanza è di chi la trova: il primo arrivato crea l'account di amministrazione. Ponete dunque INSTALL_TOKEN prima di aprire la porta:

node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
# poi in .env:
INSTALL_TOKEN=c3f1…

La procedura guidata lo confronta a tempo costante — un confronto ordinario lascerebbe indovinare il token carattere per carattere — e registra ogni rifiuto. Una volta installata l'istanza, la procedura si chiude da sé.

Installare senza interfaccia#

Per un rilascio automatizzato, ADMIN_EMAIL e ADMIN_PASSWORD creano l'amministratore all'avvio, senza passare dalla procedura guidata. Toglieteli poi: una password nell'ambiente di un servizio la legge chi legge quel servizio.

3. Permessi e dove vivono i dati#

sudo useradd --system --home /var/www/toutadmin --shell /usr/sbin/nologin toutadmin
sudo chown -R toutadmin:toutadmin /var/www/toutadmin/data
sudo chmod 750 /var/www/toutadmin/data
sudo chmod 640 /var/www/toutadmin/.env

Il codice può restare in sola lettura; solo data/ dev'essere scrivibile. SQLite scrive file vicini (-wal, -shm): conta la cartella, non solo la banca dati.

Mai su una condivisione di rete

NFS e SMB mentono sul blocco dei file. SQLite si affida a quel blocco per impedire due scritture simultanee: su una condivisione, la banca dati finisce per corrompersi senza avvisare. Disco locale, sempre — ed è il backup che parte altrove, non la banca dati.

4. Il servizio#

Avviato a mano, il prodotto si ferma quando chiudete il terminale. Affidatelo a systemd:

# /etc/systemd/system/toutadmin.service
[Unit]
Description=Toutadmin
After=network.target

[Service]
Type=simple
User=toutadmin
WorkingDirectory=/var/www/toutadmin
EnvironmentFile=/var/www/toutadmin/.env
ExecStart=/usr/bin/node src/server.js
Restart=always
RestartSec=5

# Il servizio deve scrivere soltanto in data/.
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/www/toutadmin/data

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now toutadmin
sudo systemctl status toutadmin
journalctl -u toutadmin -f

Le cinque righe d'irrobustimento non sono decorative: ProtectSystem=strict rende non scrivibile tutto il filesystem, e ReadWritePaths riapre la sola cartella che deve esserlo. Una falla di scrittura arbitraria raggiunge allora soltanto data/.

5. Il proxy inverso#

Non esponete mai la porta 3000 direttamente su internet: non fa TLS e non ha motivo di imparare a farlo.

server {
    listen 443 ssl http2;
    server_name intranet.esempio.it;

    ssl_certificate     /etc/letsencrypt/live/intranet.esempio.it/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/intranet.esempio.it/privkey.pem;

    client_max_body_size 20M;          # depositi in cassaforte, documenti ricevuti

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name intranet.esempio.it;
    return 301 https://$host$request_uri;
}

Fate inoltre in modo che il servizio ascolti soltanto sull'anello locale, perché la porta 3000 sia raggiungibile dal solo proxy.

TLS#

sudo certbot --nginx -d intranet.esempio.it

Il prodotto pone l'intestazione Strict-Transport-Security non appena la connessione è cifrata, e mai in chiaro: annunciata da una pagina non cifrata non verrebbe letta, e rinchiuderebbe una prova locale in https per sei mesi.

TRUST_PROXY va con il proxy, e solo con lui

Questa variabile fa credere al server l'indirizzo d'origine annunciato in un'intestazione. Dietro nginx è ciò che serve: altrimenti tutte le richieste sembrano venire da 127.0.0.1 e i limiti per indirizzo non proteggono più nulla. Senza proxy davanti è il contrario: chiunque annuncia l'indirizzo che vuole e aggira i limiti.

6. La procedura guidata#

Aprite il vostro dominio. Finché non esiste alcun account, ogni indirizzo rimanda a /installation. Cinque passi:

  1. La lingua dell'istanza, fra le 16.
  2. I prerequisiti, verificati e mostrati.
  3. L'azienda: il nome mostrato dappertutto.
  4. Le ferie annuali attribuite a ogni nuovo dipendente non freelance.
  5. L'account di amministrazione: indirizzo e password di almeno dodici caratteri.

Non appena esiste un account, /installation rimanda alla pagina d'accesso: la procedura si è chiusa da sé, non c'è alcun file da cancellare a mano.

7. La scansione periodica#

Diversamente dall'edizione PHP, non c'è nulla da pianificare: il server porta il proprio pianificatore, che si sveglia ogni ora e fa otto cose.

Che cosa fa la scansioneGaranzia
Chiude gli account il cui contratto è scadutoVerificato anche all'accesso e all'apertura del cruscotto
Emette le fatture degli abbonamenti in scadenzaUn indice unico impedisce di fatturare due volte
Trasforma le scadenze in notificheUna chiave di deduplicazione: una scadenza avvisa una volta sola
Ripulisce le notifiche lette e il registro di auditSecondo la conservazione scelta
Preleva la casella contabile (IMAP)Se il prelievo è configurato
Svuota la coda dei webhook, con i suoi ritentativiCinque tentativi, poi abbandono
Crea il backup automatico e lo porta fuoriQuando l'intervallo è trascorso

Ogni operazione è idempotente: una scansione ripetuta non fattura due volte e non notifica due volte. È ciò che permette di riavviare il servizio in qualunque momento senza pensarci.

8. Verificare che tutto funzioni#

# i test (non scrivono nella vostra banca dati)
npm test

# un'istanza dimostrativa, per farci un giro
node scripts/seed-demo.js

Poi, nell'interfaccia: create un membro, entrate con lui, depositate un allegato, lanciate un backup manuale e verificatelo dalla schermata Backup. Questi quattro gesti toccano la banca dati, i file, i diritti e l'archivio.

9. Aggiornare#

# 1. un backup prima, sempre — dalla schermata Backup

# 2. il codice
cd /var/www/toutadmin
git pull
npm ci --omit=dev

# 3. riavviare; la banca dati si aggiorna da sé all'avvio
sudo systemctl restart toutadmin
journalctl -u toutadmin -n 30 --no-pager

Lo schema evolve per migrazioni idempotenti: rigiocare l'aggiornamento non rompe nulla. data/ e .env non vengono mai toccati.

Tornare indietro#

Riportate il codice alla versione precedente e riavviate. Le migrazioni non cancellano colonne: una banca dati migrata resta leggibile dalla versione precedente, salvo menzione esplicita nel registro delle versioni. Nel dubbio, ripristinate l'archivio preso al passo 1.

10. Fare backup sul serio#

L'archivio tar.gz porta la banca dati e le cinque cartelle dei file. La banca dati è copiata dal backup in linea di SQLite, che produce una copia coerente anche durante una scrittura — l'edizione PHP ottiene lo stesso risultato con VACUUM INTO. Ogni file porta la sua impronta SHA-256, verificata al ripristino.

Un archivio rimasto sul server che protegge non protegge nulla: configurate una destinazione remota (FTPS o Google Drive) dalla schermata Backup, e abbonate un webhook a sauvegarde.echec per essere avvisati quando l'invio fuori fallisce. Il dettaglio è nella pagina Backup e ripristino.

Provarlo prima in locale#

npm install
cp .env.example .env
npm run dev        # riparte a ogni modifica

Aprite http://localhost:3000. In locale, lasciate NODE_ENV vuoto: in production i cookie sono marcati «sicuri» e non verranno conservati su una connessione in chiaro — girereste a vuoto sulla pagina d'accesso.

Passare all'edizione PHP, o venirne#

Le due edizioni condividono lo stesso schema — 142 tabelle, 1311 colonne — e lo stesso formato di password. Fermate l'una, copiate app.sqlite e le cartelle dei file, avviate l'altra: non c'è conversione. Vedi Le due edizioni.

Documentazione di Toutadmin — costruita il 2026-09-13. Sito autonomo, indipendente dal software.