Vai al contenuto
Q
QuoteNode

Installation

Risoluzione problemi installazione

Problemi comuni di deployment di QuoteNode relativi a link, CORS, SMTP, DNS, health check Docker e segreti persi.

Risoluzione problemi installazione

La maggior parte dei problemi di installazione derivano da mismatch di URL, SMTP mancante, porte bloccate o segreti persi. Inizia dall’URL visibile nel tuo browser e procedi verso l’interno.

Controlla quale URL è stato copiato o inviato.

  • http://localhost/... funziona solo sulla stessa macchina.
  • http://192.168.x.x/... funziona solo nella LAN o VPN. La VPN può essere un ponte valido per i team interni, ma non è una strategia di link cliente pubblico.
  • https://crm.example.com/... funziona pubblicamente solo quando DNS, HTTPS, proxy e firewall sono corretti.

Per i link cliente pubblici, effettua il deploy con un dominio pubblico usando Server pubblico Ubuntu o Coolify.

Il browser mostra errori CORS

CORS_ALLOWED_ORIGINS deve corrispondere esattamente all’origine del browser. Queste sono origini diverse:

https://crm.example.com
https://www.crm.example.com
http://crm.example.com
https://crm.example.com:8443

Correggi la variabile, ridistribuisci e riprova in una nuova sessione browser.

L’email non viene consegnata

Controlla prima questi elementi:

  • SMTP_HOST e SMTP_PORT sono corretti.
  • SMTP_AUTH=true quando il tuo provider richiede autenticazione.
  • SMTP_STARTTLS=true per la porta 587.
  • SMTP_USERNAME e SMTP_PASSWORD sono validi.
  • Il provider consente l’accesso SMTP/app-password.
  • NOTIFICATIONS_EMAIL_ENABLED=true quando le email di notifica devono essere inviate.

La condivisione manuale del link pubblico e il download PDF possono comunque funzionare senza SMTP.

Imposta:

NOTIFICATIONS_PUBLIC_PREFERENCES_URL=https://<frontend-domain>/notifications/preferences

Non lasciarlo che punti a localhost in un deployment pubblico.

I container Docker sono unhealthy

Esegui:

docker compose ps
docker compose logs -f backend
docker compose logs -f frontend
docker compose logs -f gotenberg

Cause comuni:

  • PostgreSQL si sta ancora avviando.
  • DB_PASSWORD differisce tra PostgreSQL e backend.
  • DB_ENCRYPTION_KEY è mancante o malformato.
  • Gotenberg non è healthy, quindi la generazione PDF non può avviarsi.
  • Le porte host 80/443 sono già usate da un altro servizio.

Dopo aver corretto la configurazione, riavvia il servizio interessato o lo stack:

docker compose restart backend
docker compose up -d

Il mio IP è bloccato — non riesco ad accedere

Ci sono due cause comuni: la lista IP consentiti ha bloccato l’accesso oppure la protezione anti-brute-force ha bloccato il tuo IP dopo troppi tentativi di accesso falliti.

Blocco dalla lista IP consentiti

QuoteNode ha una lista di IP consentiti configurabile in Impostazioni > Sicurezza > Lista IP consentiti (o tramite la variabile SECURITY_IP_WHITELIST). Quando è attiva, protegge l’intera superficie di amministrazione — comprese le schermate di accesso e 2FA — quindi un indirizzo escluso riceve 403 IP_NOT_ALLOWED e non può nemmeno accedere. (Gli health check e i link pubblici alle offerte per i clienti restano raggiungibili.)

Prima attendi due minuti se l’hai appena modificata nell’app. Un’attivazione o un restringimento nell’app è protetto dal blocco: deve essere riconfermato dalla tua sessione entro una breve finestra (120 s predefiniti) e, se il tuo indirizzo attuale non è incluso, la lista torna automaticamente a quella precedente. Un autoblocco accidentale dalla schermata Sicurezza si risolve quindi da solo — attendi e riprova. (Questa rete di sicurezza non si applica alla variabile SECURITY_IP_WHITELIST, che viene applicata all’avvio.)

Recupero (override dell’operatore): se sei ancora bloccato — es. una SECURITY_IP_WHITELIST dell’operatore, una lista memorizzata non valida o non puoi attendere — aggiungi questa variabile all’ambiente del container backend (.env o sezione environment: nel docker-compose) e riavvia:

SECURITY_IP_WHITELIST_FORCE_DISABLE=true
docker compose restart backend

Questo disabilita completamente la lista consentiti e permette l’accesso. Una volta dentro, correggi la lista in Impostazioni > Sicurezza, poi rimuovi SECURITY_IP_WHITELIST_FORCE_DISABLE e riavvia di nuovo.

Non lasciare SECURITY_IP_WHITELIST_FORCE_DISABLE=true in produzione. Disabilita l’intero controllo accessi per IP.

Blocco dalla protezione anti-brute-force

QuoteNode blocca gli indirizzi IP dopo troppi tentativi di accesso falliti in un’ora:

Soglia Fallimenti in 1 h Effetto
Soft 10 Ritardo progressivo (1 s per tentativo aggiuntivo, max. 30 s)
Blocco temporaneo 20 Bloccato per 5 minuti
Blocco permanente 50 Bloccato fino allo sblocco manuale

Come sbloccare:

  1. Pannello admin — un altro amministratore apre Impostazioni > Dashboard Sicurezza > IP bloccati e clicca Sblocca.
  2. Attendere — i blocchi temporanei scadono dopo SECURITY_TEMP_LOCK_MINUTES (5 minuti predefiniti). I blocchi permanenti non scadono da soli.
  3. Alzare temporaneamente le soglie se sei bloccato e nessun altro admin può aiutare. Aggiungi al .env, riavvia, accedi, poi rimuovi:
SECURITY_MAX_FAILED_LOGINS_SOFT=9999
SECURITY_MAX_FAILED_LOGINS_TEMP_LOCK=9999
SECURITY_MAX_FAILED_LOGINS_HARD_LOCK=9999

Ripristina i valori predefiniti dopo aver recuperato l’accesso.

Il rate limiter dei link pubblici blocca gli indirizzi IP che superano i limiti di tentativi. Questi blocchi sono in memoria e scadono dopo PUBLIC_RATE_LIMIT_IP_BLOCK_MINUTES (60 minuti predefiniti) o dopo un riavvio del backend.

L’aggiornamento si ferma con “cannot enforce offer-section ownership”

Dopo un aggiornamento il backend non si avvia e docker compose logs backend mostra:

Migration V145 cannot enforce offer-section ownership: 3 offer_items row(s) point at a
section of another offer or tenant.

Che cosa significa

Un’offerta è composta da sezioni (“Hardware”, “Servizi”) e dalle righe collocate al loro interno. Questa versione rende vincolante quel collegamento: una riga può stare solo in una sezione della propria offerta. Alcune righe vecchie nel vostro database violano la regola, quindi l’aggiornamento si ferma e chiede a voi di decidere, invece di riscrivere le offerte da solo.

Nulla è rotto e il rifiuto stesso non ha modificato nulla. Questa migrazione è stata annullata completamente: nessuna modifica applicata a metà e nulla da riparare nella cronologia delle migrazioni. Tenete però presente che migrazioni precedenti dello stesso aggiornamento potrebbero essere già state applicate: il database si trova appena prima di questo passo, non necessariamente alla versione di partenza. Ciò significa anche che i container precedenti potrebbero funzionarci o no, a seconda di che cosa quelle migrazioni hanno cambiato: non contate sul tornare alle vecchie immagini come via d’uscita.

Scegliete che cosa deve accadere

Opzione Che cosa fa Che cosa cambia per voi
Controlla il problema (predefinito) Elenca le offerte e le righe interessate. Nulla. Sola lettura.
Scollega le assegnazioni errate Cancella la sezione solo di quelle righe. Le righe restano nella loro offerta con lo stesso prezzo, quantità e descrizione. Perdono l’intestazione di sezione e appaiono come righe non raggruppate.
Scegli una sezione per ogni riga Chiede riga per riga e propone solo sezioni della sua stessa offerta. Mantenete il raggruppamento desiderato. Le righe saltate restano irrisolte e l’aggiornamento si fermerà di nuovo.

Le offerte già inviate ai clienti continuano a mostrare esattamente ciò che il cliente ha visto: il contenuto di un’offerta inviata viene congelato al momento dell’invio, quindi né il link pubblico né il PDF cambiano.

Individuate prima l’installazione giusta (Coolify o più stack su un server)

Ogni comando qui sotto deve colpire la vostra installazione. Un server può ospitarne diverse, e più di una può avere un servizio chiamato backend: elencate quindi prima le candidate — ogni riga è un’installazione, con il nome del progetto e il file Compose da cui è stata avviata:

docker inspect $(docker ps -aq --filter "label=com.docker.compose.service=backend" \
                             --filter "label=com.docker.compose.oneoff=False") \
  --format '{{index .Config.Labels "com.docker.compose.project"}} | {{index .Config.Labels "com.docker.compose.project.config_files"}}'

Se ottenete più di una riga, scegliete la vostra; non tirate a indovinare. Poi eseguite ogni comando nominando esplicitamente quel progetto e quel file, invece di un docker compose secco, così da non agire sull’installazione sbagliata:

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections

Sostituite <project> e <compose-file> con i valori della vostra riga e teneteli in ogni comando che segue: è questo che fa agire ciascuno sulla vostra installazione e non su un’altra. Se la riga mostra più file separati da virgole, ripetete -f per ciascuno, in quell’ordine. Se l’elenco è vuoto, i container non sono stati avviati con Docker Compose: usate la directory indicata dal vostro provider o dalla guida di installazione, oppure chiedete a chi ha configurato il server. Su Coolify fatelo in un terminale sul server, non sul container: il terminale del container richiede un backend in esecuzione, e il vostro non lo è. (I messaggi dello strumento mostrano un docker compose … breve senza queste opzioni, per restare leggibili: quando li copiate, aggiungete i vostri -p e -f.)

Prima il backup

PostgreSQL resta in funzione anche quando il backend non parte, quindi potete fare il backup senza l’applicazione. Il comando legge nome del database e utente dal container stesso, quindi funziona comunque li abbiate chiamati, e nessuna password compare a schermo:

docker compose -p <project> -f <compose-file> exec -T postgres sh -c \
  'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" --format=custom -f /tmp/before-repair.dump \
   && pg_restore --list /tmp/before-repair.dump | head -3'
docker compose -p <project> -f <compose-file> cp postgres:/tmp/before-repair.dump ./before-repair.dump
ls -lh before-repair.dump

La riga pg_restore --list stampa le prime voci dell’archivio: se le vedete e ls -lh mostra un file non vuoto, il backup è leggibile. Conservate before-repair.dump accanto al vostro docker-compose.yml: si trova sul disco del server, non dentro un container, quindi sopravvive a tutto ciò che segue.

Controllare il problema

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections

Questo comando si limita a leggere. Mostra le offerte interessate, che cosa non va in ogni riga e le opzioni:

Found 3 offer item(s) in 2 offer(s) assigned to a section that does not belong to that offer.

  Offer OF-2026-014 [DRAFT]
    item:    Mounting frame
    section: section "Installation" — section belongs to another offer

Anteprima, poi applicazione

Senza --apply=true ogni comando è un’anteprima: mostra esattamente che cosa farebbe e termina senza aver cambiato nulla, in qualsiasi terminale.

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections --plan=unlink

Aggiungete --apply=true --acknowledge-backup=true per eseguirlo. È l’unica forma che scrive:

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections --plan=unlink --apply=true --acknowledge-backup=true

Per decidere voi stessi ogni riga usate --plan=choose. Lo strumento pone una domanda per riga ed elenca le sezioni di quell’offerta, così non digitate mai un identificativo del database. Senza --apply=true continua solo a mostrarvi il piano:

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections --plan=choose --apply=true --acknowledge-backup=true

Completare l’aggiornamento

Quando lo strumento segnala che nessuna riga punta più a una sezione sbagliata:

docker compose -p <project> -f <compose-file> up -d

La migrazione bloccata non ha lasciato nulla in sospeso, quindi al prossimo avvio viene semplicemente eseguita. Non serve alcun comando flyway repair o baseline — e non dovete usarlo, perché nasconderebbe il problema dei dati invece di risolverlo.

Verificate come sempre: aprite /health, poi controllate la nuova versione in Admin > Licenza.

Se dice che i dati sono cambiati dopo la vostra anteprima

Lo strumento applica il piano che avete approvato, non ciò che trova nel momento in cui scrive. Se durante la conferma è comparsa un’assegnazione errata, qualcosa si è spostato o una sezione che avevate scelto è stata eliminata, rifiuta l’intera riparazione e non cambia nulla. Rieseguite il controllo per vedere lo stato attuale, poi rifate la vostra scelta.

Se dice che la riparazione è stata salvata — o che non ha potuto confermarlo

La riparazione avviene in un’unica transazione, quindi o viene salvata per intero o non viene salvata affatto. Due finali richiedono attenzione. Se fallisce un passo successivo — la scrittura del resoconto o la rilettura che conferma il risultato — lo strumento mantiene la parola SAVED nel messaggio: la riparazione ha funzionato, quindi non ripristinate il backup. Se invece compare UNCONFIRMED, il database non ha mai risposto e nessuno può dire come sia andata; non ripristinate il backup e non ripetete la riparazione. In entrambi i casi rieseguite il controllo in sola lettura: se segnala che non c’è nulla da riparare, avete finito e potete avviare lo stack; se elenca ancora le stesse righe, non è stato salvato nulla e potete rifare la vostra scelta.

docker compose -p <project> -f <compose-file> run --rm backend --repair-offer-sections

Una cosa da leggere con attenzione: se avete usato Scegli una sezione per ogni riga e ne avete saltate alcune, quelle righe saltate continueranno a comparire nel controllo. È previsto e non significa che la riparazione sia fallita. Confrontate l’elenco con le righe che avevate davvero scelto di sistemare: se restano solo quelle saltate, la riparazione ha funzionato e ciò che resta è la decisione che avete rimandato — rieseguite lo strumento per esse quando volete. Se compaiono ancora righe che avevate scelto di sistemare, non è stato salvato nulla.

Che cosa è stato cambiato e cosa fare se continua a fallire

Ogni esecuzione che modifica qualcosa scrive gli identificativi di riga, offerta e sezione interessate in /app/data/backups/ del volume del backend. Non contiene dati dei clienti e può essere allegato a una richiesta di assistenza. Se il file non può essere scritto, lo strumento stampa lo stesso resoconto a schermo: copiatelo prima di chiudere il terminale:

docker compose -p <project> -f <compose-file> run --rm --entrypoint sh backend \
  -c 'ls -1 /app/data/backups/offer-section-repair-*.json'

Se l’aggiornamento si ferma ancora:

  • eseguite di nuovo il controllo: le righe saltate compaiono ancora;
  • se il messaggio nomina un’altra migrazione, questo strumento non la riguarda. Ripara solo le assegnazioni di sezione delle righe d’offerta;
  • se il messaggio è invariato e il controllo non segnala nulla da riparare, segnalate il problema allegando il log del backend e il resoconto qui sopra.

Il certificato HTTPS non viene emesso

Per i deployment pubblici:

  • Il DNS deve puntare al server di deployment.
  • Le porte 80 e 443 devono essere raggiungibili da internet.
  • Solo un proxy dovrebbe terminare HTTPS per lo stesso dominio.
  • In Coolify, assegna il dominio al servizio frontend.

Ho perso .env

Se hai perso .env, fermati e valuta prima di ruotare i valori.

  • DB_PASSWORD può solitamente essere reimpostato con accesso amministrativo PostgreSQL.
  • TIMING_TOKEN_SECRET e PUBLIC_LINK_PASSWORD_SESSION_SECRET possono essere rigenerati, ma le sessioni/token esistenti potrebbero scadere.
  • DB_ENCRYPTION_KEY è critico. Senza la chiave originale, i valori cifrati non possono essere decifrati.
  • Se la cifratura PII e abilitata, i campi cliente/contatto cifrati dipendono da DB_ENCRYPTION_KEY.

Ripristina .env dal backup ogniqualvolta possibile.

Ripristino se .env è perso ma esiste un backup del database

  1. Crea un nuovo .env con nuovi segreti.
  2. Ripristina il database dal tuo backup (pg_dump / pg_restore).
  3. Tutti i dati di business (clienti, offerte, prodotti, utenti) saranno completamente accessibili.
  4. Solo pochi campi vengono persi: codici MFA (gli utenti si re-iscrivono), password SMTP (reinserire nelle impostazioni), chiave API FX (reinserire). Se ENCRYPT_PII=true era abilitato, anche i dati cliente cifrati vengono persi.

Se i tuoi backup sono cifrati con age (impostazione predefinita), hai bisogno del file di identità age dal tuo kit di recupero per decifrarli. Consulta la guida Backup e Ripristino.

Password admin dimenticata

Usa un percorso di recupero non distruttivo:

  1. Se SMTP è configurato, usa la pagina di accesso: Password dimenticata?
  2. Se un altro amministratore può ancora accedere, apri Utenti nel pannello admin e reimposta la password.
  3. Se nessun amministratore riesce ad accedere e il flusso di reset via email non è disponibile, ripristina l’accesso da un backup funzionante o usa la tua procedura di manutenzione interna.

docker compose down -v non e una procedura di reset della password. Elimina tutti i dati — utenti, offerte, clienti, impostazioni e backup. Usalo solo per ambienti di test usa-e-getta.

La verifica della licenza fallisce

Per i deployment reali, mantieni:

LICENSE_SKIP_SIGNATURE_CHECK=false

Se una licenza valida fallisce comunque, controlla che il deployment abbia la versione app attesa e un orologio di sistema sincronizzato con NTP. Conferma che la licenza corrisponda all’edizione/versione distribuita, quindi contatta il supporto o sostituisci il file di licenza tramite il percorso operatore documentato. Non impostare il bypass della firma a true come workaround di produzione.

Last reviewed: Recently