2023-03-09 09:13:15 -08:00
import type {
ITriggerFunctions ,
IDataObject ,
INodeType ,
INodeTypeDescription ,
ITriggerResponse ,
} from 'n8n-workflow' ;
2021-11-03 08:02:20 -07:00
import { watch } from 'chokidar' ;
export class LocalFileTrigger implements INodeType {
description : INodeTypeDescription = {
displayName : 'Local File Trigger' ,
name : 'localFileTrigger' ,
icon : 'fa:folder-open' ,
group : [ 'trigger' ] ,
version : 1 ,
subtitle : '=Path: {{$parameter["path"]}}' ,
description : 'Triggers a workflow on file system changes' ,
:sparkles: Improve Waiting Webhook call state in WF Canvas (#2430)
* N8N-2586 Improve Waiting Webhook call state in WF Canvas
* N8N-2586 Added watcher for showing Webhook's Node Tooltip on execution
* N8N-2586 Show helping tooltip for trigger node if wokrflow is running, it is a trigger node, if it is only one trigger node in WF
* N8N-2586 Rework/Move logic to computed property, Created getter for ActveTriggerNodesInWokrflow, Add style to trigger node's tooltip, remove comments
* N8N-2586 Added EventTriggerDescription prop in INodeTypeDescription Interface, Updated Logic for TriggerNode Tooltip based on the new prop
* N8N-2586 Add new use cases/watcher to show Trigger Nodes Tooltip / If has issues, if paused, if wokrlfow is running, Refactor Getter
* N8N-2586 Added z-index to tooltip, Added new Scenario for Tooltip if it is Draged&Droped on the WF
* N8N-2586 Refactor computed property for draged nodes
* N8N-2586 Fixed Conflicts
* N8N-2586 Fixed Tooltip
* N8N-2586 Dont show tooltip on core trigger nodes that execute automatically
* N8N-2586 Fixed Webhook tooltip when adding/deleting canvas during WF execution
* N8N-2586 Updated Logic, Simplify the code
* N8N-2586 Simplify Code
* N8N-2586 Added check for nodetype
* update dragging to use local state
* N8N-2586 Added eventTriggerDescription to Interval Node
* add comment, use new getter
* update to always check
Co-authored-by: Mutasem <mutdmour@gmail.com>
2021-11-25 14:33:41 -08:00
eventTriggerDescription : '' ,
2021-11-03 08:02:20 -07:00
defaults : {
name : 'Local File Trigger' ,
color : '#404040' ,
} ,
2023-06-23 03:29:24 -07:00
triggerPanel : {
header : '' ,
executionsHelp : {
inactive :
"<b>While building your workflow</b>, click the 'listen' button, then make a change to your watched file or folder. This will trigger an execution, which will show up in this editor.<br /> <br /><b>Once you're happy with your workflow</b>, <a data-key='activate'>activate</a> it. Then every time a change is detected, the workflow will execute. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor." ,
active :
"<b>While building your workflow</b>, click the 'listen' button, then make a change to your watched file or folder. This will trigger an execution, which will show up in this editor.<br /> <br /><b>Your workflow will also execute automatically</b>, since it's activated. Every time a change is detected, this node will trigger an execution. These executions will show up in the <a data-key='executions'>executions list</a>, but not in the editor." ,
} ,
activationHint :
"Once you’ ve finished building your workflow, <a data-key='activate'>activate</a> it to have it also listen continuously (you just won’ t see those executions here)." ,
} ,
2021-11-03 08:02:20 -07:00
inputs : [ ] ,
outputs : [ 'main' ] ,
properties : [
{
2022-06-03 10:23:49 -07:00
displayName : 'Trigger On' ,
2021-11-03 08:02:20 -07:00
name : 'triggerOn' ,
type : 'options' ,
options : [
{
name : 'Changes to a Specific File' ,
value : 'file' ,
} ,
{
name : 'Changes Involving a Specific Folder' ,
value : 'folder' ,
} ,
] ,
required : true ,
default : '' ,
} ,
{
displayName : 'File to Watch' ,
name : 'path' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
triggerOn : [ 'file' ] ,
2021-11-03 08:02:20 -07:00
} ,
} ,
default : '' ,
placeholder : '/data/invoices/1.pdf' ,
} ,
{
displayName : 'Folder to Watch' ,
name : 'path' ,
type : 'string' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
triggerOn : [ 'folder' ] ,
2021-11-03 08:02:20 -07:00
} ,
} ,
default : '' ,
placeholder : '/data/invoices' ,
} ,
{
displayName : 'Watch for' ,
name : 'events' ,
type : 'multiOptions' ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
triggerOn : [ 'folder' ] ,
2021-11-03 08:02:20 -07:00
} ,
} ,
options : [
{
name : 'File Added' ,
value : 'add' ,
description : 'Triggers whenever a new file was added' ,
} ,
{
name : 'File Changed' ,
value : 'change' ,
description : 'Triggers whenever a file was changed' ,
} ,
{
name : 'File Deleted' ,
value : 'unlink' ,
description : 'Triggers whenever a file was deleted' ,
} ,
{
name : 'Folder Added' ,
value : 'addDir' ,
description : 'Triggers whenever a new folder was added' ,
} ,
{
name : 'Folder Deleted' ,
value : 'unlinkDir' ,
description : 'Triggers whenever a folder was deleted' ,
} ,
] ,
required : true ,
default : [ ] ,
description : 'The events to listen to' ,
} ,
{
displayName : 'Options' ,
name : 'options' ,
type : 'collection' ,
placeholder : 'Add Option' ,
default : { } ,
options : [
2023-12-11 04:03:28 -08:00
{
displayName : 'Await Write Finish' ,
name : 'awaitWriteFinish' ,
type : 'boolean' ,
default : false ,
description : 'Whether to wait until files finished writing to avoid partially read' ,
} ,
2021-11-03 08:02:20 -07:00
{
displayName : 'Include Linked Files/Folders' ,
name : 'followSymlinks' ,
type : 'boolean' ,
default : true ,
2022-08-17 08:50:24 -07:00
description :
'Whether linked files/folders will also be watched (this includes symlinks, aliases on MacOS and shortcuts on Windows). Otherwise only the links themselves will be monitored).' ,
2021-11-03 08:02:20 -07:00
} ,
{
displayName : 'Ignore' ,
name : 'ignored' ,
type : 'string' ,
default : '' ,
placeholder : '**/*.txt' ,
2022-08-17 08:50:24 -07:00
description :
'Files or paths to ignore. The whole path is tested, not just the filename. Supports <a href="https://github.com/micromatch/anymatch">Anymatch</a>- syntax.' ,
2021-11-03 08:02:20 -07:00
} ,
2023-12-11 04:03:28 -08:00
{
displayName : 'Ignore Existing Files/Folders' ,
name : 'ignoreInitial' ,
type : 'boolean' ,
default : true ,
description : 'Whether to ignore existing files/folders to not trigger an event' ,
} ,
2021-11-03 08:02:20 -07:00
{
displayName : 'Max Folder Depth' ,
name : 'depth' ,
type : 'options' ,
options : [
{
2022-06-03 10:23:49 -07:00
name : '1 Levels Down' ,
value : 1 ,
2021-11-03 08:02:20 -07:00
} ,
{
2022-06-03 10:23:49 -07:00
name : '2 Levels Down' ,
value : 2 ,
2021-11-03 08:02:20 -07:00
} ,
{
name : '3 Levels Down' ,
value : 3 ,
} ,
{
2022-06-03 10:23:49 -07:00
name : '4 Levels Down' ,
value : 4 ,
2021-11-03 08:02:20 -07:00
} ,
{
2022-06-03 10:23:49 -07:00
name : '5 Levels Down' ,
value : 5 ,
2021-11-03 08:02:20 -07:00
} ,
{
name : 'Top Folder Only' ,
value : 0 ,
} ,
2022-06-03 10:23:49 -07:00
{
name : 'Unlimited' ,
value : - 1 ,
} ,
2021-11-03 08:02:20 -07:00
] ,
default : - 1 ,
description : 'How deep into the folder structure to watch for changes' ,
} ,
2023-12-11 04:03:28 -08:00
{
displayName : 'Use Polling' ,
name : 'usePolling' ,
type : 'boolean' ,
default : false ,
description :
'Whether to use polling for watching. Typically necessary to successfully watch files over a network.' ,
} ,
2021-11-03 08:02:20 -07:00
] ,
} ,
] ,
} ;
async trigger ( this : ITriggerFunctions ) : Promise < ITriggerResponse > {
const triggerOn = this . getNodeParameter ( 'triggerOn' ) as string ;
const path = this . getNodeParameter ( 'path' ) as string ;
const options = this . getNodeParameter ( 'options' , { } ) as IDataObject ;
let events : string [ ] ;
if ( triggerOn === 'file' ) {
2022-08-17 08:50:24 -07:00
events = [ 'change' ] ;
2021-11-03 08:02:20 -07:00
} else {
events = this . getNodeParameter ( 'events' , [ ] ) as string [ ] ;
}
const watcher = watch ( path , {
2022-12-06 01:57:07 -08:00
ignored : options.ignored === '' ? undefined : options . ignored ,
2021-11-03 08:02:20 -07:00
persistent : true ,
2023-12-11 04:03:28 -08:00
ignoreInitial :
options . ignoreInitial === undefined ? true : ( options . ignoreInitial as boolean ) ,
2022-08-17 08:50:24 -07:00
followSymlinks :
options . followSymlinks === undefined ? true : ( options . followSymlinks as boolean ) ,
depth : [ - 1 , undefined ] . includes ( options . depth as number )
? undefined
: ( options . depth as number ) ,
2023-12-11 04:03:28 -08:00
usePolling : options.usePolling as boolean ,
awaitWriteFinish : options.awaitWriteFinish as boolean ,
2021-11-03 08:02:20 -07:00
} ) ;
2022-12-02 12:54:28 -08:00
const executeTrigger = ( event : string , pathString : string ) = > {
this . emit ( [ this . helpers . returnJsonArray ( [ { event , path : pathString } ] ) ] ) ;
2021-11-03 08:02:20 -07:00
} ;
for ( const eventName of events ) {
2023-02-27 19:39:43 -08:00
watcher . on ( eventName , ( pathString ) = > executeTrigger ( eventName , pathString as string ) ) ;
2021-11-03 08:02:20 -07:00
}
2022-12-02 12:54:28 -08:00
async function closeFunction() {
2021-11-03 08:02:20 -07:00
return watcher . close ( ) ;
}
return {
closeFunction ,
} ;
}
}