- Kde API funguje
- Předpoklady
- Připojení k serveru s heslem z Trezoru
- Přehled operací
- Příklad 1: odeslání přílohy z úkolu na FTP
- Příklad 2: stažení jednoho souboru z FTP do DMS
- Příklad 3: dávkové stažení souborů ze SFTP do DMS
- Životní cyklus připojení a výkon
- FTP versus SFTP: na co si dát pozor
- Časté chyby
- Checklist před nasazením
- Alternativa bez výpočtu: SmartEvent (od v5.19)
FTP a SFTP použití ve výpočtech
- Kde API funguje
- Předpoklady
- Připojení k serveru s heslem z Trezoru
- Přehled operací
- Příklad 1: odeslání přílohy z úkolu na FTP
- Příklad 2: stažení jednoho souboru z FTP do DMS
- Příklad 3: dávkové stažení souborů ze SFTP do DMS
- Životní cyklus připojení a výkon
- FTP versus SFTP: na co si dát pozor
- Časté chyby
- Checklist před nasazením
- Alternativa bez výpočtu: SmartEvent (od v5.19)
Návod pro konzultanty, kteří potřebují z výpočtu na úkolu odeslat soubor na FTP nebo SFTP server, případně ho odtud stáhnout. Přístupové heslo se drží v Trezoru, takže není nikdy zapsané v textu šablony. Nahrazuje dřívější řešení přes curl.* (od v5.17) – tyto metody byly odstraněny a validace šablony na ně upozorní textem „For FTP/SFTP calls use FtpApi."
Kde API funguje
Objekty ftp a vault jsou vložené pouze do sandboxu výpočtů na úkolu (Task JS).
Kontext výpočtu |
|
Výpočet na úkolu nebo na tlačítku (Task JS) | ano |
Výpočet na případu (Process JS) | ne |
Identity výpočet | ne |
Konzole výpočtů | ne |
vault.get()) a metoda getFtpClientWithVaultPassword() vyžadují TAS 5.17 nebo novější. Na starších instalacích si nejdřív ověřte verzi.Předpoklady
Než začnete psát výpočet, musí být hotové dvě věci na straně administrátora.
1. Heslo v Trezoru
V Administrace → Trezor vytvořte položku typu secret a pojmenujte ji tak, aby bylo jasné, ke kterému systému patří – například ftp_partner_password. Ve výpočtu se na ni pak odkážete názvem.
{systém}_{protokol}_{účel}, tedy partner_ftp_password nebo eshop_sftp_import. Jedna položka na jeden servisní účet.2. Povolené cesty v allowedExternalSources
Každá cesta předaná do FTP API – včetně vzdálené cesty na serveru – prochází kontrolou povolených zdrojů. Bez zapsání cílové cesty do konfigurace skončí výpočet chybou BANNED_SOURCE („Accessing this source is not allowed!").
- konfigurační klíč:
application.allowedExternalSources - env proměnná:
TAS_DMS_ALLOWED_EXTERNAL_SOURCES, hodnoty oddělené znakem| - v administraci je pole jen pro čtení – mění se přes prostředí, ne v UI
TAS_DMS_ALLOWED_EXTERNAL_SOURCES=/tas/faktury|/out/objednavky|/data
Chování kontroly, které je dobré znát:
- porovnává se prefix cesty, takže
/datapovolí i/data/2026/x.csv, ale zároveň i/database/...– volte cesty co nejkonkrétnější, - automaticky povolené jsou podsložky interního TMP (
<paths.tmp>/apicall,/uploads,/pdf,/zip,/generated) a asset rooty, paths.storagePathpovolený není – pokud do něj chcete stahovat, musí být v konfiguraci.
<paths.tmp>/apicall/...). Ta je povolená automaticky a nemusíte kvůli ní zasahovat do konfigurace prostředí.Připojení k serveru s heslem z Trezoru
Nejdřív si vyzvedněte referenci na heslo, pak s ní vytvořte klienta. Konfigurace se předává bez klíče password.
const passwordRef = vault.get('ftp_partner_password');
const client = ftp.getFtpClientWithVaultPassword({
protocol: 'ftp',
host: 'ftp.partner.cz',
port: 21,
username: 'tas-export',
secure: false,
}, passwordRef);passwordRef je neprůhledná reference. Nelze ji vypsat do logu ani serializovat – JSON.stringify() i toString() vrátí jen [VaultSecretRef]. Heslo se rozbalí až uvnitř FTP API.
Varianta pro SFTP se liší jen protokolem a portem:
const passwordRef = vault.get('sftp_partner_password');
const client = ftp.getFtpClientWithVaultPassword({
protocol: 'sftp',
host: 'sftp.partner.cz',
port: 22,
username: 'tas-import',
timeout: 30000,
}, passwordRef);ftp.getFtpClient(config), kde se heslo předává v otevřeném textu. Použitelné je jen pro rychlý test nebo pro server bez autentizace – takové heslo je součástí šablony procesu a uvidí ho každý, kdo ji smí editovat. Do produkce vždy variantu s Trezorem.Klíče konfigurace
Klíč | Povinný | Platí pro | Popis |
| ano | oba |
|
| ano | oba | hostname nebo IP serveru |
| ne | oba | výchozí 21 pro FTP, 22 pro SFTP |
| prakticky ano | oba | servisní účet; u FTP bez uvedení jde o anonymní přístup |
| ne | FTP |
|
| ne | FTP | TLS volby Node.js ( |
| ne | SFTP | timeout připojení v ms, výchozí |
| ne | SFTP | privátní klíč v PEM formátu a jeho heslo |
privateKey nelze načíst z Trezoru – vault reference je použitelná pouze jako heslo. Pro autentizaci klíčem by klíč musel být v textu výpočtu, proto preferujte SFTP s heslem z Trezoru. U nešifrovaného FTP (secure: false) jdou přihlašovací údaje po síti v otevřeném textu – mimo interní síť použijte SFTP nebo secure: true.Přehled operací
Metoda | Vrací | Poznámka |
| pole objektů |
|
| text |
|
| text |
|
| text | maže soubor, ne složku |
| boolean | u FTP a SFTP se chová odlišně, viz níže |
| cesta / text | idempotentní – nad existující složkou nechybuje |
| cesta / text | u FTP maže včetně obsahu bez ohledu na parametr |
| cesta / text | slouží i k přesunu mezi složkami |
226 Transfer complete.), SFTP konektor cestu nebo hlášení knihovny. Spolehlivý signál úspěchu je jediný: volání nevyhodilo výjimku.Co přijímá upload()
source je odkaz na soubor v DMS aktuálního případu, ne cesta na disku. Přijímá:
- proměnnou typu příloha –
vars['Faktura'], - ID souboru v DMS (číslo nebo číselný string),
- název souboru v rámci aktuálního případu.
Soubor musí patřit aktuálnímu případu (nebo jeho nadřízenému či podřízenému), jinak operace skončí chybou Dms file does not belong to process. Pokud chcete poslat soubor vytvořený ve výpočtu na disku, uložte ho nejdřív do DMS přes storage.saveToDms(filePath, fileName) a vrácené ID předejte do upload().
Příklad 1: odeslání přílohy z úkolu na FTP
Výpočet na tlačítku „Odeslat partnerovi". Založí složku podle ID případu, odešle přílohu z proměnné a výsledek zapíše do stavové proměnné.
const passwordRef = vault.get('ftp_partner_password');
const client = ftp.getFtpClientWithVaultPassword({
protocol: 'ftp',
host: 'ftp.partner.cz',
port: 21,
username: 'tas-export',
secure: false,
}, passwordRef);
const remoteDir = `/tas/faktury/${lib.iprocId()}`;
const remoteFile = `${remoteDir}/faktura.pdf`;
try {
client.createDirectory(remoteDir);
client.upload(vars['Faktura'], remoteFile);
vars['StavOdeslani'].setValue('Odesláno');
proc.info('Invoice uploaded to FTP', { remoteFile });
} catch (err) {
vars['StavOdeslani'].setValue('Chyba odeslání');
proc.error('FTP upload failed', { err: err.message, remoteFile });
debug.error('Odeslání faktury na FTP se nezdařilo. Kontaktujte správce systému.');
}Předpoklady: v allowedExternalSources je /tas/faktury, v Trezoru položka ftp_partner_password, v šabloně proměnné Faktura (příloha) a StavOdeslani (text).
debug.error() zablokuje dokončení úkolu a zprávu zobrazí uživateli – vhodné, když odeslání je podmínkou pro pokračování procesu. Pokud má proces běžet dál, debug.error() vynechte a spolehněte se na stavovou proměnnou a proc.error(). Chybu nikdy nepolykejte tiše.Více příloh v jedné proměnné
Pokud proměnná obsahuje víc souborů, upload() s předanou proměnnou pošle jen první a do logu zapíše varování. Pro všechny soubory si vytáhněte ID a názvy a projděte je v cyklu:
const ids = lib.getDMSFileIds('*', '|').split('|').filter(Boolean);
const names = lib.getDMSFileNames('*', '|').split('|').filter(Boolean);
ids.forEach((id, index) => {
client.upload(id, `${remoteDir}/${names[index]}`);
});'*' vrací všechny přílohy případu. Novější verze TAS umí filtrovat jen soubory z konkrétní proměnné třetím parametrem – lib.getDMSFileIds('', ';', vars['Prilohy'].getValue()). Podporu na své instalaci si ověřte.Příklad 2: stažení jednoho souboru z FTP do DMS
Opačný směr. Výpočet stáhne soubor podle názvu z proměnné do pracovní složky v TMP a odtud ho uloží jako přílohu případu.
const fileName = vars['NazevSouboru'].getValue();
const remoteDir = `/tas/faktury/${lib.iprocId()}`;
const passwordRef = vault.get('ftp_partner_password');
const client = ftp.getFtpClientWithVaultPassword({
protocol: 'ftp',
host: 'ftp.partner.cz',
port: 21,
username: 'tas-export',
secure: false,
}, passwordRef);
const paths = storage.getPaths();
const remoteFile = `${remoteDir}/${fileName}`;
const workDir = `${paths.tmp}/apicall/ftp-${lib.iprocId()}`;
const localFile = `${workDir}/${fileName}`;
try {
storage.createDir(workDir);
client.download(remoteFile, localFile);
const dmsId = storage.saveToDms(localFile, fileName);
proc.info('File downloaded from FTP', { remoteFile, localFile, dmsId });
} catch (err) {
proc.error('FTP download failed', { err: err.message, remoteFile });
debug.error(`Soubor ${fileName} se nepodařilo stáhnout z FTP.`);
}
Předpoklady: v allowedExternalSources je /tas/faktury, cílová složka je v TMP (apicall, povolená automaticky), v Trezoru položka ftp_partner_password, v šabloně proměnná NazevSouboru (text).
localFile je vždy plná cesta včetně jména souboru, ne jen adresář. Bez uvedení by se použil paths.storagePath, který navíc nemusí být v povolených zdrojích. Cesta přes storage.getPaths() je nezávislá na konkrétní instalaci.Příklad 3: dávkové stažení souborů ze SFTP do DMS
Výpočet stáhne všechny CSV soubory z /out/objednavky, uloží je jako přílohy případu a na serveru je přesune do podsložky processed, aby se nezpracovaly znovu.
const passwordRef = vault.get('sftp_partner_password');
const client = ftp.getFtpClientWithVaultPassword({
protocol: 'sftp',
host: 'sftp.partner.cz',
port: 22,
username: 'tas-import',
timeout: 30000,
}, passwordRef);
const sourceDir = '/out/objednavky';
const archiveDir = `${sourceDir}/processed`;
const paths = storage.getPaths();
const workDir = `${paths.tmp}/apicall/objednavky-${lib.iprocId()}`;
storage.createDir(workDir);
try {
const files = client.list(sourceDir);
const csvFiles = files.filter((file) => file.type === 'file' && file.name.toLowerCase().endsWith('.csv'));
client.createDirectory(archiveDir);
let imported = 0;
for (const file of csvFiles) {
const source = `${sourceDir}/${file.name}`;
const target = `${workDir}/${file.name}`;
client.download(source, target);
storage.saveToDms(target, file.name);
client.rename(source, `${archiveDir}/${file.name}`);
imported += 1;
}
vars['PocetImportovanych'].setValue(imported);
proc.info('Import from SFTP finished', { imported, sourceDir });
} catch (err) {
vars['StavImportu'].setValue('Chyba importu');
proc.error('SFTP import failed', { err: err.message, sourceDir });
}Předpoklady: v allowedExternalSources je /out/objednavky (pokryje i podsložku processed), TMP složka apicall je povolená automaticky, v Trezoru je položka sftp_partner_password.
Životní cyklus připojení a výkon
Každé volání metody otevře nové připojení, provede operaci a připojení zavře. Neexistuje sdílená session mezi voláními, takže:
- cyklus přes 200 souborů znamená 200 přihlášení – počítejte s tím u serverů s limitem souběžných spojení nebo s rate-limitem,
- dávky zpracovávejte sekvenčně cyklem
for ... of, nikdy paralelně, - u velkých přenosů hrozí timeout výpočtu – větší dávky řešte plánováním, ne výpočtem na tlačítku.
FTP versus SFTP: na co si dát pozor
Chování | SFTP | FTP |
| dotaz na existenci objektu – | testuje velikost souboru větší než 0 – |
| parametr | zakládá vždy rekurzivně, parametr se ignoruje |
| nad neprázdnou složkou bez | maže složku včetně obsahu bez ohledu na parametr |
Metadata z | plná – včetně |
|
| respektuje se, výchozí | ignoruje se, platí výchozí hodnota knihovny |
list(). Nad FTP také nepoužívejte exists() pro test existence složky; volejte přímo createDirectory(), které nad existující složkou nechybuje.Časté chyby
Chyba | Příčina | Řešení |
| cesta není v | doplnit cestu do |
| do FTP API šlo něco jiného než výstup | referenci předávat bez jakékoli úpravy, nepřevádět na string |
| nedostupný host, špatný port, firewall | ověřit host a port a průchodnost ze serveru TASu |
|
| zkontrolovat ID nebo název přílohy |
| soubor patří jinému případu | pracovat se souborem aktuálního případu |
| vzdálená cesta neexistuje | ověřit přes |
| v šabloně zůstalo staré volání | přepsat na |
Chyby se propagují jako výjimka a zapisují se do logu výpočtů. Operace upload a delete se logují na úrovni info, ostatní na trace.
Checklist před nasazením
- Kód neobsahuje
await,asyncaniPromise. - Heslo je v Trezoru a výpočet používá
getFtpClientWithVaultPassword(), ne heslo v textu. - Výpočet je na úkolu (Task JS), ne na případu.
- Cílová vzdálená i lokální cesta je v
TAS_DMS_ALLOWED_EXTERNAL_SOURCES, nebo jde o TMPapicall. download()má explicitní plnou cestu k cílovému souboru včetně jména.- ID případu se čte přes
lib.iprocId(). - U FTP se nespoléhá na
exists()pro složky ani na parametrrecursive. - Operace jsou v
try/catcha chyba se propíše do stavové proměnné nebo přesproc.error(). - U dávek je odhadnutý počet spojení a doba běhu.
Alternativa bez výpočtu: SmartEvent (od v5.19)
Pokud stačí jedna operace bez další logiky, nemusíte psát výpočet – na šablonu úkolu lze navěsit SmartEvent SmartEventFtpRequester. Konfiguruje se formulářem a podporuje stejné operace jako výpočtové API: list, upload, download, delete, exists, createDirectory, removeDirectory, rename. Heslo se v konfiguraci zadává jako {{vault:nazev-polozky}} a vyhodnotí se z Trezoru.
Výpočet volte, když potřebujete rozhodovací logiku, cyklus přes více souborů nebo zápis výsledku do proměnných a DMS.
Updated
by Frantisek Brych