1717module . exports = function ( RED ) {
1818 "use strict" ;
1919 var fs = require ( 'fs' ) ;
20+ var minimatch = require ( "minimatch" ) ;
21+
22+ function AmazonS3InNode ( n ) {
23+ RED . nodes . createNode ( this , n ) ;
24+ this . awsConfig = RED . nodes . getNode ( n . aws ) ;
25+ this . region = n . region ;
26+ this . bucket = n . bucket ;
27+ this . filepattern = n . filepattern || "" ;
28+ var node = this ;
29+ var AWS = this . awsConfig ? this . awsConfig . AWS : null ;
30+ if ( ! AWS ) {
31+ node . warn ( "Missing AWS credentials" ) ;
32+ return ;
33+ }
34+ var s3 = new AWS . S3 ( ) ;
35+ node . status ( { fill :"blue" , shape :"dot" , text :"initializing" } ) ;
36+ s3 . listObjects ( { Bucket : node . bucket } , function ( err , data ) {
37+ if ( err ) {
38+ node . error ( "failed to fetch S3 state: " + err ) ;
39+ node . status ( { fill :"red" , shape :"ring" , text :"error" } ) ;
40+ return ;
41+ }
42+ node . state = data . Contents . filter ( function ( e ) {
43+ return ! node . filepattern || minimatch ( e , node . filepattern ) ;
44+ } ) . map ( function ( e ) {
45+ return e . Key ;
46+ } ) ;
47+ node . status ( { } ) ;
48+ node . on ( "input" , function ( msg ) {
49+ node . status ( { fill :"blue" , shape :"dot" , text :"checking for changes" } ) ;
50+ s3 . listObjects ( { Bucket : node . bucket } , function ( err , data ) {
51+ if ( err ) {
52+ node . warn ( "failed to fetch S3 state: " + err ) ;
53+ node . status ( { } ) ;
54+ return ;
55+ }
56+ node . status ( { } ) ;
57+ var newState = data . Contents . filter ( function ( e ) {
58+ return ! node . filepattern ||
59+ minimatch ( e , node . filepattern ) ;
60+ } ) . map ( function ( e ) {
61+ return e . Key ;
62+ } ) ;
63+ var seen = { } ;
64+ var i ;
65+ for ( i = 0 ; i < node . state . length ; i ++ ) {
66+ seen [ node . state [ i ] ] = true ;
67+ }
68+ for ( i = 0 ; i < newState . length ; i ++ ) {
69+ if ( seen [ newState [ i ] ] ) {
70+ delete seen [ newState [ i ] ] ;
71+ } else {
72+ msg . payload = newState [ i ] ;
73+ msg . file = newState [ i ] . substring ( newState [ i ] . lastIndexOf ( '/' ) + 1 ) ;
74+ msg . event = 'add' ;
75+ node . send ( msg ) ;
76+ }
77+ }
78+ for ( var k in seen ) {
79+ if ( seen . hasOwnProperty ( k ) ) {
80+ msg . payload = k ;
81+ msg . file = k . substring ( k . lastIndexOf ( '/' ) + 1 ) ;
82+ msg . event = 'delete' ;
83+ node . send ( msg ) ;
84+ }
85+ }
86+ node . state = newState ;
87+ } ) ;
88+ } ) ;
89+ var interval = setInterval ( function ( ) {
90+ node . emit ( "input" , { } ) ;
91+ } , 900000 ) ; // 15 minutes
92+ node . on ( "close" , function ( ) {
93+ if ( interval !== null ) {
94+ clearInterval ( interval ) ;
95+ }
96+ } ) ;
97+ } ) ;
98+ }
99+ RED . nodes . registerType ( "amazon s3 in" , AmazonS3InNode ) ;
100+
101+ function AmazonS3QueryNode ( n ) {
102+ RED . nodes . createNode ( this , n ) ;
103+ this . awsConfig = RED . nodes . getNode ( n . aws ) ;
104+ this . region = n . region ;
105+ this . bucket = n . bucket ;
106+ this . filename = n . filename || "" ;
107+ var node = this ;
108+ var AWS = this . awsConfig ? this . awsConfig . AWS : null ;
109+ if ( ! AWS ) {
110+ node . warn ( "Missing AWS credentials" ) ;
111+ return ;
112+ }
113+ var s3 = new AWS . S3 ( ) ;
114+ node . on ( "input" , function ( msg ) {
115+ var bucket = node . bucket || msg . bucket ;
116+ if ( bucket === "" ) {
117+ node . warn ( "No bucket specified" ) ;
118+ return ;
119+ }
120+ var filename = node . filename || msg . filename ;
121+ if ( filename === "" ) {
122+ node . warn ( "No filename specified" ) ;
123+ return ;
124+ }
125+ msg . bucket = bucket ;
126+ msg . filename = filename ;
127+ node . status ( { fill :"blue" , shape :"dot" , text :"downloading" } ) ;
128+ s3 . getObject ( {
129+ Bucket : bucket ,
130+ Key : filename ,
131+ } , function ( err , data ) {
132+ if ( err ) {
133+ node . warn ( "download failed " + err . toString ( ) ) ;
134+ delete msg . payload ;
135+ msg . error = err ;
136+ } else {
137+ msg . payload = data . Body ;
138+ delete msg . error ;
139+ }
140+ node . status ( { } ) ;
141+ node . send ( msg ) ;
142+ } ) ;
143+ } ) ;
144+ }
145+ RED . nodes . registerType ( "amazon s3" , AmazonS3QueryNode ) ;
20146
21147 function AmazonS3OutNode ( n ) {
22148 RED . nodes . createNode ( this , n ) ;
@@ -26,29 +152,33 @@ module.exports = function(RED) {
26152 this . filename = n . filename || "" ;
27153 this . localFilename = n . localFilename || "" ;
28154 var node = this ;
29- var AWS = this . awsConfig . AWS ;
155+ var AWS = this . awsConfig ? this . awsConfig . AWS : null ;
156+ if ( ! AWS ) {
157+ node . warn ( "Missing AWS credentials" ) ;
158+ return ;
159+ }
30160 if ( AWS ) {
31161 var s3 = new AWS . S3 ( ) ;
32162 node . status ( { fill :"blue" , shape :"dot" , text :"checking credentials" } ) ;
33- s3 . listObjects ( { Bucket : this . bucket } , function ( err ) {
163+ s3 . listObjects ( { Bucket : node . bucket } , function ( err ) {
34164 if ( err ) {
35165 node . error ( "AWS S3 error: " + err ) ;
36166 node . status ( { fill :"red" , shape :"ring" , text :"error" } ) ;
37167 return ;
38168 }
39169 node . status ( { } ) ;
40170 node . on ( "input" , function ( msg ) {
41- var bucket = this . bucket || msg . bucket ;
171+ var bucket = node . bucket || msg . bucket ;
42172 if ( bucket === "" ) {
43173 node . warn ( "No bucket specified" ) ;
44174 return ;
45175 }
46- var filename = this . filename || msg . filename ;
176+ var filename = node . filename || msg . filename ;
47177 if ( filename === "" ) {
48178 node . warn ( "No filename specified" ) ;
49179 return ;
50180 }
51- var localFilename = this . localFilename || msg . localFilename ;
181+ var localFilename = node . localFilename || msg . localFilename ;
52182 if ( localFilename ) {
53183 // TODO: use chunked upload for large files
54184 node . status ( { fill :"blue" , shape :"dot" , text :"uploading" } ) ;
0 commit comments