Skip to content

Commit 08e9851

Browse files
author
Nuwan Chandrasoma
committed
initial commit
0 parents  commit 08e9851

28 files changed

+32661
-0
lines changed

.firebaserc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"projects": {
3+
"default": "react-at-uom"
4+
}
5+
}

.gitignore

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# See https://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
6+
# testing
7+
/coverage
8+
9+
# production
10+
/build
11+
12+
# misc
13+
.DS_Store
14+
.env.local
15+
.env.development.local
16+
.env.test.local
17+
.env.production.local
18+
19+
npm-debug.log*
20+
yarn-debug.log*
21+
yarn-error.log*
22+
functions/service-account-credentials.json

README.md

+2,229
Large diffs are not rendered by default.

database.rules.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"rules": {
3+
".read": "auth != null",
4+
".write": "auth != null"
5+
}
6+
}

firebase.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"database": {
3+
"rules": "database.rules.json"
4+
},
5+
"firestore": {
6+
"rules": "firestore.rules",
7+
"indexes": "firestore.indexes.json"
8+
},
9+
"hosting": {
10+
"public": "build",
11+
"ignore": [
12+
"firebase.json",
13+
"**/.*",
14+
"**/node_modules/**"
15+
],
16+
"rewrites": [
17+
{
18+
"source": "**",
19+
"destination": "/index.html"
20+
}
21+
]
22+
},
23+
"storage": {
24+
"rules": "storage.rules"
25+
}
26+
}

firestore.indexes.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
// Example:
3+
//
4+
// "indexes": [
5+
// {
6+
// "collectionId": "widgets",
7+
// "fields": [
8+
// { "fieldPath": "foo", "mode": "ASCENDING" },
9+
// { "fieldPath": "bar", "mode": "DESCENDING" }
10+
// ]
11+
// }
12+
// ]
13+
"indexes": []
14+
}

firestore.rules

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
service cloud.firestore {
2+
match /databases/{database}/documents {
3+
match /{document=**} {
4+
allow read, write;
5+
}
6+
}
7+
}

functions/.gitignore

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# See https://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
6+
# testing
7+
/coverage
8+
9+
# production
10+
/build
11+
12+
# misc
13+
.DS_Store
14+
.env.local
15+
.env.development.local
16+
.env.test.local
17+
.env.production.local
18+
19+
npm-debug.log*
20+
yarn-debug.log*
21+
yarn-error.log*

functions/index.js

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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

Comments
 (0)