Skip to content

0015. Storage FTP/FTPS basato su basic-ftp

  • Stato: Accettata
  • Data: 2026-07-09
  • Decisori: Team BI/ETL

Contesto

Alcuni gestionali depositano i file di scambio via FTP/FTPS invece di SFTP. Serve uno StorageProvider che si componga con i codec del File connector (vedi ADR-0007): elenco directory, download/upload in streaming, supporto sia a FTP in chiaro sia a FTPS. L' ADR-0011 aveva già scartato basic-ftp per SFTP (protocollo diverso) rimandando la scelta a questa issue (#30).

Decisione

Adottiamo basic-ftp (client FTP/FTPS puro JS, nessuna dipendenza nativa).

  • list(prefix): elenca i file della directory prefix (non ricorsivo), filtrando su FileInfo.isFile.
  • get: download in streaming verso un PassThrough (come SFTP), nessun file intero in memoria.
  • put: upload in streaming (Readable.from(data)), creando le directory mancanti (ensureDir). A differenza di ssh2-sftp-client.mkdir, ensureDir di basic-ftp cambia la working directory del client verso la dir creata; per non lasciare stato residuo a sorpresa su un client iniettato e riusato, put naviga sempre in modo esplicito (cd("/") prima e dopo l'operazione) invece di affidarsi alla cwd lasciata da una chiamata precedente.
  • FTPS opt-in esplicito: secure è false di default (FTP in chiaro), true per FTPS esplicita (AUTH TLS), "implicit" per FTPS implicita (legacy). Un default "sicuro a sorpresa" romperebbe l'uso immediato con i gestionali FTP legacy che restano il caso d'uso più comune per questo connettore.
  • Il client è iniettato (DI, come gli altri storage): loadFtpConfig(prefix) costruisce la config da env (FTP_HOST/PORT/USER/PASSWORD/SECURE).
  • Test: solo unit test, con un client basic-ftp mockato (ftp.test.ts) che copre list/get/put e la gestione degli errori. A differenza di SFTP/S3, non c'è un test di integrazione reale in questo giro (nessun servizio Docker ftp-test): è una deviazione consapevole dal criterio di accettazione originale dell'issue, accettata per contenere lo scope. Il path I/O reale (incluso il negoziato TLS di FTPS) resta quindi verificato solo dal mock, non da un server reale.

Alternative considerate

  • ssh2-sftp-client — è per SFTP (SSH), non FTP/FTPS: protocollo diverso, non applicabile qui (vedi ADR-0011).
  • Client FTP nativi Node più datati (jsftp, ftp) — meno mantenuti, supporto FTPS più debole o assente; basic-ftp è l'opzione moderna con supporto FTPS esplicita/implicita di prima classe e API a promesse.
  • Test di integrazione con server Docker reale (come sftp-test) — scartata per questo giro per contenere lo scope; resta un'estensione naturale futura se emergono bug non catturabili col solo mock.

Conseguenze

  • Positive: list/get/put FTP/FTPS con streaming, nessuna dipendenza nativa, API allineata allo StorageProvider esistente (stesso modello di SFTP/S3).
  • Costi/limiti: list non ricorsivo (coerente col resto dei connettori); nessuna copertura I/O reale (solo mock) finché non viene aggiunto un test di integrazione dedicato; put paga il costo di due cd extra per operazione per garantire uno stato prevedibile del client condiviso.

Noeva è un marchio registrato di 4D S.R.L.