Problemi comuni
Un playbook pratico per i problemi che si presentano più spesso in produzione. Ogni voce segue lo stesso schema: sintomo, controlli, soluzione.
Blocco login (5 tentativi falliti / 15 minuti)
Sintomo: una password corretta viene rifiutata e la risposta contiene errors.auth.tooManyAttempts con i minuti rimanenti. Si tratta del blocco per-account, non del rate limiter per IP (un 429 con errors.general.rateLimitExceeded indica invece il limiter).
Controlli:
- Il contatore di blocco risiede nella tabella
LoginAttempt: una riga per email tentata conFailedCount,LockedUntileLastAttemptAt. Poiché è nel database, i container blue-green e i riavvii condividono la stessa vista. - SuperAdmin: Impostazioni > Accessi ed errori (
/dashboard/amministrazione/settings/accessi-errori, API/api/v1/auth-events) registra gli eventi di autenticazione mascherati, inclusoaccount_locked. - I log dell'app mostrano un warning mascherato (
Login attempt for locked account) con i minuti rimanenti.
Soluzione:
- Attendi la finestra di 15 minuti. Il contatore riparte allo scadere della finestra e un login riuscito elimina la riga.
- Non eliminare né modificare a mano le righe di blocco: il blocco è temporale e il successivo tentativo fallito ricreerebbe semplicemente lo stato.
- Se i blocchi si ripetono senza che l'utente riprovi, trattalo come credential stuffing: ispeziona gli eventi di autenticazione e i log del rate-limit, e ruota la password.
Email non recapitata
Sintomo: un utente non riceve un'email di attivazione, OTP, recupero password o newsletter.
Controlli:
- L'invio transazionale è condizionato dal feature flag
enable_email_sender. - Configurazione del provider:
EMAIL_SENDER_EMAIL_PROVIDERpiù le sue credenziali. In sviluppo, un provider vuoto significa modalità console (i messaggi sono loggati, non inviati). Vedi Configurazione email. - L'invio massivo/newsletter usa il provider selezionato nelle impostazioni di Comunicazione (DB), con il default d'ambiente come fallback. Un provider con quota esaurita (429/402) parcheggia il job fino alla successiva mezzanotte UTC, e una allow-list di email di test non vuota redirige ogni messaggio solo a quegli indirizzi.
- Lista di soppressione:
newsletter_suppressionscontiene i destinatari permanentemente esclusi (bounce, complaint, manuale, unsubscribe). La coda di invio marca gli indirizzi soppressi comeSuppressed, così inviati + falliti + soppressi quadrano con il totale del job. - Log di consegna: Impostazioni > Email inviate (SuperAdmin,
/api/v1/email-delivery-logs) unisce il log locale (retention 180 giorni) con le analytics Cloudflare live quando configurate. Filtra per destinatario, stato, provider e intervallo date. - Log del container per errori del provider (
flo logs app <tenant>, oppure il visualizzatore log app in Impostazioni e Audit).
Soluzione:
- Correggi le variabili d'ambiente del provider e riavvia il container: le variabili d'ambiente sono lette all'avvio.
- Per gli alias Gmail, verifica la configurazione "Invia come"; per SMTP personalizzato, controlla host/porta/TLS e credenziali.
- Per le newsletter, riseleziona il provider o impostane le credenziali; riprova dopo il reset della quota se il provider era parcheggiato.
- Valuta il motivo di soppressione prima di rimuovere una soppressione: un unsubscribe è una preferenza legittima, non un fallimento di consegna.
- Rigenera i messaggi transazionali falliti con
POST /api/v1/maintenance/email/replay-failede l'headerX-Maintenance-Key(Maintenance__ApiKey). SupportadryRune filtri per destinatario/utente, e risponde503quando nessun provider email è risolvibile oppure rifiuta quandoenable_email_senderè spento (errors.emailDeliveryLog.replayEmailDisabled).
Problemi con i webhook di pagamento
Sintomo: il provider riporta un pagamento riuscito ma l'ordine resta in sospeso, oppure il webhook risponde 400 con errors.payment.invalidWebhook.
Controlli:
- URL webhook configurato presso il provider:
POST https://<dominio>/api/v1/payments/{provider}/webhook, dove{provider}è il gateway configurato (stripeopaypal, corrispondente aPAYMENT__PROVIDER). L'endpoint è anonimo, limita il body a 64 KB ed è rate-limitato a 120/min per IP. - Verifica della firma: Stripe usa
PAYMENT__STRIPE__WEBHOOKSECRET(il valorewhsec_) contro l'headerStripe-Signature; PayPal usaPAYMENT__PAYPAL__WEBHOOKIDper la verifica della firma. Un segreto errato o mancante fa fallire il parsing, che emerge comeerrors.payment.invalidWebhook. - Dedup ed elaborazione: le righe
PaymentWebhookEventregistrano id evento esterno, tipo evento, timestamp di ricezione/elaborazione ed eventuali messaggi di errore. - Ciclo di vita ordini: gli ordini di prenotazione in sospeso/trattenuti scadono secondo pianificazione quando nessun webhook li conferma.
Soluzione:
- Copia il segreto di firma dalla dashboard del provider nella variabile d'ambiente, riavvia il container e re-invia l'evento dal provider.
- Ordini Commerce (piano studio):
POST /api/v1/commerce/orders/{id}/reconcilerilegge il gateway e aggiorna l'ordine. - Ordini di pagamento prenotazioni: risolvi un ordine in revisione con
POST /api/v1/payments/admin/orders/{id}/review; ritenta l'emissione fattura elettronica conPOST /api/v1/payments/orders/{id}/invoice/retry(disponibile quando il job fattura èNeedsReviewoFailed). - Verifica che il gateway sia configurato:
GET /api/v1/payments/admin/readiness.
Modalità manutenzione
Sintomo: le API protette rispondono 503 con errors.general.maintenanceMode.
Controlli:
flo --vps production instance maintenance <tenant> statusrestituisceenabled,activeRequestseleaseBoundMaintenanceBlockedper ogni backend.- La variabile d'ambiente
MAINTENANCE_MODEè il default al boot; una commutazione live la sovrascrive per il processo in esecuzione senza riavvio.
Soluzione:
- Disattivala:
flo --vps production instance maintenance <tenant> off. Il comando verifica lo stato risultante su ogni backend. - Se viene riportato
leaseBoundMaintenanceBlocked, MON controlla il tenant: completaflo failover auto disarmprima di modificare lo stato di manutenzione. - Se una commutazione fallisce e lo stato non può essere verificato, la CLI ferma i backend per bloccare il traffico. Trattalo come incidente operativo e ispeziona lo stato del cutover e ogni container prima di avviare qualsiasi cosa.
- Per aggiornare la cache dei feature-flag con la manutenzione attiva, usa
flo --vps production instance maintenance-cache-invalidate <tenant>.
Avvisi di failover e lease
Sintomo: un tenant sullo standby smette di servire, gli health check falliscono oppure i log riportano dinieghi di lease (lease-missing, lease-expired, lease-invalid-signature, lease-fenced, lease-stale-epoch e motivi simili). Un valore non valido di FAILOVER_ENABLED rifiuta l'avvio invece di disarmare silenziosamente un nodo.
Controlli:
flo control whoami
flo --vps <node> vps stats
flo --vps <node> failover status
flo --vps <node> failover monitor --all -y
flo --vps <node> failover incident list
- Leggi il record durevole prima di agire:
flo failover incident show <id>, poiflo failover incident explain <id>.
Soluzione:
incident resume <id> -ysolo dopo una nuova sonda di sicurezza live;incident abort <id> -ysolo dopo aver provato il recupero di sorgente e standby.- Non eliminare mai incidenti, lease, epoch o file di fence per far passare un drill, e non modificare a mano il database MON.
- Il failback resta manuale:
flo --vps <standby> failover back <tenant> --to <primary> -y, poi ricrea la protezione conflo --vps <primary> failover replication setup <tenant> -y. - Le distribuzioni rifiutano tenant fenced e tenant senza primary lease pubblicata; correggi lo stato DR invece di aggirare il controllo.
Runbook operatore completo: Failover e Disaster Recovery.