1
+ const functions = require ( "firebase-functions" ) ;
2
+ // Include a Service Account Key to use a Signed URL
3
+ const gcs = require ( '@google-cloud/storage' ) ( { keyFilename : 'service-account-credentials.json' } ) ;
4
+ //const vision = require('@google-cloud/vision')({projectId:'react-at-uom' ,keyFilename: 'service-account-credentials.json'});
5
+ const vision = require ( '@google-cloud/vision' ) ;
6
+ const Filter = require ( "bad-words" ) ;
7
+ const spawn = require ( "child-process-promise" ) . spawn ;
8
+ const badWordsFilter = new Filter ( ) ;
9
+ const path = require ( "path" ) ;
10
+ const os = require ( "os" ) ;
11
+ const fs = require ( "fs" ) ;
12
+ const admin = require ( 'firebase-admin' ) ;
13
+ admin . initializeApp ( functions . config ( ) . firebase ) ;
14
+
15
+ exports . moderator = functions . firestore
16
+ . document ( "/comments/{commentsId}" )
17
+ . onWrite ( event => {
18
+ const comment = event . data . data ( ) ;
19
+ if ( comment && ! comment . sanitized ) {
20
+ console . log ( "Retrieved comment content: " , comment ) ;
21
+ const moderatedComment = moderateComment ( comment . text ) ;
22
+ console . log (
23
+ "Comment has been moderated. Saving to DB: " ,
24
+ moderatedComment
25
+ ) ;
26
+ return event . data . ref . set (
27
+ {
28
+ text : moderatedComment ,
29
+ sanitized : true
30
+ } ,
31
+ { merge : true }
32
+ ) ;
33
+ }
34
+ } ) ;
35
+
36
+ exports . generateThumbnail = functions . storage . object ( ) . onChange ( event => {
37
+ const object = event . data ;
38
+
39
+ const fileBucket = object . bucket ; // The Storage bucket that contains the file.
40
+ const filePath = object . name ; // File path in the bucket.
41
+ const contentType = object . contentType ; // File content type.
42
+ const resourceState = object . resourceState ; // The resourceState is 'exists' or 'not_exists' (for file/folder deletions).
43
+ const metageneration = object . metageneration ; // Number of times metadata has been generated. New objects have a value of 1.
44
+
45
+ if ( ! contentType . startsWith ( "image/" ) ) {
46
+ console . log ( "This is not an image." ) ;
47
+ return ;
48
+ }
49
+
50
+ // Get the file name.
51
+ const fileName = path . basename ( filePath ) ;
52
+ // Exit if the image is already a thumbnail.
53
+ if ( fileName . startsWith ( "thumb_" ) ) {
54
+ console . log ( "Already a Thumbnail." ) ;
55
+ return ;
56
+ }
57
+
58
+ // Exit if this is a move or deletion event.
59
+ if ( resourceState === "not_exists" ) {
60
+ console . log ( "This is a deletion event." ) ;
61
+ return ;
62
+ }
63
+
64
+ // Exit if file exists but is not new and is only being triggered
65
+ // because of a metadata change.
66
+ if ( resourceState === "exists" && metageneration > 1 ) {
67
+ console . log ( "This is a metadata change event." ) ;
68
+ return ;
69
+ }
70
+ // [END stopConditions]
71
+
72
+ // [START thumbnailGeneration]
73
+ // Download file from bucket.
74
+ const bucket = gcs . bucket ( fileBucket ) ;
75
+ const tempFilePath = path . join ( os . tmpdir ( ) , fileName ) ;
76
+ const metadata = { contentType : contentType } ;
77
+ // We add a 'thumb_' prefix to thumbnails file name. That's where we'll upload the thumbnail.
78
+ const thumbFileName = `thumb_${ fileName } ` ;
79
+ const thumbFilePath = path . join ( path . dirname ( filePath ) , thumbFileName ) ;
80
+ const thumbFile = bucket . file ( thumbFilePath ) ;
81
+
82
+ return bucket
83
+ . file ( filePath )
84
+ . download ( {
85
+ destination : tempFilePath
86
+ } )
87
+ . then ( ( ) => {
88
+ console . log ( "Image downloaded locally to" , tempFilePath ) ;
89
+ // Generate a thumbnail using ImageMagick.
90
+ return spawn ( "convert" , [
91
+ tempFilePath ,
92
+ "-thumbnail" ,
93
+ "200x200>" ,
94
+ tempFilePath
95
+ ] ) ;
96
+ } )
97
+ . then ( ( ) => {
98
+ console . log ( "Thumbnail created at" , tempFilePath ) ;
99
+ // Uploading the thumbnail.
100
+ return bucket . upload ( tempFilePath , {
101
+ destination : thumbFilePath ,
102
+ metadata : metadata
103
+ } ) ;
104
+ // Once the thumbnail has been uploaded delete the local file to free up disk space.
105
+ } )
106
+ . then ( ( file ) => {
107
+ fs . unlinkSync ( tempFilePath ) ;
108
+ bucket . file ( filePath ) . delete ( ) . then ( ( ) => {
109
+ console . log ( 'original file deleted to free-up space' ) ;
110
+ } ) ;
111
+ // Get the Signed URLs for the thumbnail and original image.
112
+ const config = {
113
+ action : 'read' ,
114
+ expires : '03-01-2500'
115
+ } ;
116
+ //console.log('info:',fileBucket,thumbFile.name);
117
+ return thumbFile . getSignedUrl ( config ) ;
118
+ } )
119
+ . then ( ( url ) => {
120
+ console . log ( 'signed url:' , url [ 0 ] ) ;
121
+ const id = fileName . split ( "." ) [ 0 ] ;
122
+ const gcsUrl = "gs://" + fileBucket + "/" + thumbFile . name ;
123
+
124
+ const client = new vision . ImageAnnotatorClient ( ) ;
125
+ client . labelDetection ( gcsUrl )
126
+ . then ( results => {
127
+ const labels = results [ 0 ] . labelAnnotations ;
128
+ let labelDescriptions = [ ] ;
129
+
130
+ //console.log('Labels:');
131
+ labels . forEach ( label => labelDescriptions . push ( label . description ) ) ;
132
+ return admin . firestore ( ) . collection ( "comments" ) . doc ( id ) . set ( {
133
+ labels : labelDescriptions
134
+ } , { merge : true } ) ;
135
+
136
+ } )
137
+ . catch ( err => {
138
+ console . error ( 'ERROR:' , err ) ;
139
+ } ) ;
140
+
141
+ return admin . firestore ( ) . collection ( "comments" ) . doc ( id ) . set ( {
142
+ avatar : url [ 0 ]
143
+ } , { merge : true } ) ;
144
+ } )
145
+ } ) ;
146
+
147
+ function moderateComment ( comment ) {
148
+ // Moderate if the user uses SwearWords.
149
+ if ( containsSwearwords ( comment ) ) {
150
+ console . log ( "User is swearing. moderating..." ) ;
151
+ comment = moderateSwearwords ( comment ) ;
152
+ }
153
+
154
+ return comment ;
155
+ }
156
+
157
+ // Returns true if the string contains swearwords.
158
+ function containsSwearwords ( comment ) {
159
+ return comment !== badWordsFilter . clean ( comment ) ;
160
+ }
161
+
162
+ // Hide all swearwords. e.g: Crap => ****.
163
+ function moderateSwearwords ( comment ) {
164
+ return badWordsFilter . clean ( comment ) ;
165
+ }
0 commit comments