diff --git a/.gitignore b/.gitignore index a02f2e1..1802d91 100644 --- a/.gitignore +++ b/.gitignore @@ -70,8 +70,7 @@ web_modules/ # dotenv environment variables file .env -.env.test - +*.env # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache diff --git a/README.md b/README.md index 040701c..9eba8e3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Image Classification with Coligo and Visual Recognition +# Image Classification with Code Engine and Visual Recognition :warning: WORK IN PROGRESS diff --git a/backend/package-lock.json b/backend/package-lock.json index 8d090f4..b1a7d42 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,5 +1,5 @@ { - "name": "coligo-object-detection-backend", + "name": "codeengine-image-classification-backend", "version": "1.0.0", "lockfileVersion": 1, "requires": true, diff --git a/backend/package.json b/backend/package.json index f385273..a11f94d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,7 +1,7 @@ { - "name": "coligo-object-detection-backend", + "name": "codeengine-image-classification-backend", "version": "1.0.0", - "description": "Backend server for Coligo Image Classification", + "description": "Backend server for Code Engine Image Classification", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/backend/server.js b/backend/server.js index 5ea339d..6897a8a 100644 --- a/backend/server.js +++ b/backend/server.js @@ -13,125 +13,220 @@ const cors = require("cors"); app.use(cors()); const port = process.env.PORT || 3001; +/** + *Define Cloud OBject Storage client configuration + * + * @return {*} cosCLient + */ +function getCosClient() { + var config = { + endpoint: + process.env.COS_ENDPOINT || + "s3.us-south.cloud-object-storage.appdomain.cloud", + apiKeyId: process.env.COS_SECRET_APIKEY, + ibmAuthEndpoint: "https://iam.cloud.ibm.com/identity/token", + serviceInstanceId: process.env.COS_SECRET_RESOURCE_INSTANCE_ID, + }; - -function getCosClient(){ - var config = { - endpoint: - process.env.COS_ENDPOINT || - "s3.us-south.cloud-object-storage.appdomain.cloud", - apiKeyId: process.env.COS_SECRET_APIKEY, - ibmAuthEndpoint: "https://iam.cloud.ibm.com/identity/token", - serviceInstanceId: process.env.COS_SECRET_RESOURCE_INSTANCE_ID, -}; - -var cosClient = new myCOS.S3(config); -return cosClient; + //console.log(process.env); + var cosClient = new myCOS.S3(config); + return cosClient; } -// To upload files to Cloud Object Storage +/** + * Upload images to COS Bucket + * + * @param {*} req + * @param {*} res + * @param {*} next + */ function uploadFilesToCOS(req, res, next) { -var upload = multer({ - storage: multerS3({ - s3: getCosClient(), - bucket: process.env.COS_BUCKETNAME+'/images', - metadata: function (req, file, cb) { - cb(null, { fieldName: file.fieldname }); - }, - key: function (req, file, cb) { - cb(null, file.originalname); - }, - }), -}).array("files", 10); + var upload = multer({ + storage: multerS3({ + s3: getCosClient(), + bucket: process.env.COS_BUCKETNAME + "/images", + metadata: function (req, file, cb) { + cb(null, { fieldName: file.fieldname }); + }, + key: function (req, file, cb) { + cb(null, file.originalname); + }, + }), + }).array("files", 10); upload(req, res, function (err) { if (err) { - next(err); + return next(err); } - if (req.files.length > 1) { - res.send( - "Successfully uploaded " + req.files.length + " files to Object Storage" + if (req.files.length === 0) { + return res.send("Upload an image..."); + } else if (req.files.length > 1) { + return res.send( + "Successfully uploaded " + req.files.length + " images to Object Storage" + ); + } else { + return res.send( + "Successfully uploaded " + req.files.length + " image to Object Storage" ); - } - else{ - res.send( - "Successfully uploaded " + req.files.length + " file to Object Storage" - ); } }); } - -function getBucketContents(req, res, next) { +/** + *Get COS bucket contents (images) + * + * @param {*} req + * @param {*} res + * @param {*} next + * @param {*} prefix + * @return {*} result dictionary + */ +async function getBucketContents(req, res, next, prefix) { + try { let cos = getCosClient(); let bucketName = process.env.COS_BUCKETNAME; var resultDict = {}; var result; console.log(`Retrieving bucket contents from: ${bucketName}`); - return cos.listObjects( - {Bucket: bucketName, - Prefix: 'results'}, - ).promise() - .then(async (data) => { - if (data != null && data.Contents != null) { - for (var i = 0; i < data.Contents.length; i++) { - var itemKey = data.Contents[i].Key; - var itemSize = data.Contents[i].Size; - console.log(`Item: ${itemKey} (${itemSize} bytes).`) - result = await getItem(bucketName, itemKey); - resultDict[itemKey] = result; - } - res.send(resultDict); - } - }) - .catch((e) => { - console.error(`ERROR: ${e.code} - ${e.message}\n`); - }); - + + const data = await cos + .listObjects({ + Bucket: bucketName, + Prefix: prefix, + }) + .promise(); + if (data != null && data.Contents != null) { + for (var i = 0; i < data.Contents.length; i++) { + var itemKey = data.Contents[i].Key; + var itemSize = data.Contents[i].Size; + console.log(`Item: ${itemKey} (${itemSize} bytes).`); + result = await getItem(bucketName, itemKey, prefix); + resultDict[itemKey] = result; + } + res.send(resultDict); + } + } catch (e) { + console.error(`ERROR: ${e.code} - ${e.message}\n`); + return next(e.message); + } +} +/** + * Get each item in a COS Bucket + * + * @param {*} bucketName + * @param {*} itemName + * @param {*} prefix + * @return {*} + */ +async function getItem(bucketName, itemName, prefix) { + let cos = getCosClient(); + console.log(`Retrieving item from bucket: ${bucketName}, key: ${itemName}`); + try { + const data = await cos + .getObject({ + Bucket: bucketName, + Key: itemName, + }) + .promise(); + if (data != null) { + if (prefix === "results") { + return JSON.parse(data.Body); + } else { + return Buffer.from(data.Body).toString("base64"); + } + } + } catch (e) { + console.error(`ERROR: ${e.code} - ${e.message}\n`); + } } -function getItem(bucketName, itemName) { - let cos = getCosClient(); - let result; - console.log(`Retrieving item from bucket: ${bucketName}, key: ${itemName}`); - return cos.getObject({ - Bucket: bucketName, - Key: itemName - }).promise() - .then((data) => { - if (data != null) { - //console.log('File Contents: ' + Buffer.from(data.Body).toString()); - return JSON.parse(data.Body); - } - }) - .catch((e) => { - console.error(`ERROR: ${e.code} - ${e.message}\n`); - }); +async function deleteItem(req, res, next, bucketName, itemName, prefix) { + let cos = getCosClient(); + let bucketname = process.env.COS_BUCKETNAME; + itemName = prefix + "/" + itemName; + if (prefix === "results") { + itemName = itemName + ".json"; + } + console.log(`Deleting item: ${itemName}`); + try { + await cos + .deleteObject({ + Bucket: bucketname, + Key: itemName, + }) + .promise(); + console.log(`Item: ${itemName} deleted!`); + res.send(`Item: ${itemName} deleted!`); + } catch (e) { + console.error(`ERROR: ${e.code} - ${e.message}\n`); + } } + /* * Default route for the web app */ -app.get("/", function (req, res) { +app.get("/", function (req, res, next) { res.send("Hello World! from backend"); }); + +app.get("/items", async (req, res, next) => { + try { + var prefix = req.query.prefix; + // console.log(prefix); + await getBucketContents(req, res, next, prefix); + } catch (error) { + // Passes errors into the error handler + return next(error); + } +}); /* * Upload an image for Image classification */ -app.post("/images", uploadFilesToCOS, function(req, res, next) { - next(); +app.post("/images", uploadFilesToCOS, function (req, res, next) {}); + +/** +* Get the JSON from the results folder of COS Bucket + */ +app.post("/results", async (req, res, next) => { + try { + await getBucketContents(req, res, next, "results"); + } catch (error) { + // Passes errors into the error handler + console.log(error); + return next(error); + } }); -app.post("/results", getBucketContents, function(req, res, next) { - next(); +/** +* Delete an item from the COS Bucket + */ +app.delete("/item", async (req, res, next) => { + var itemName = req.query.filename; + console.log(itemName); + await deleteItem(req, res, next, null, itemName, "images"); + await deleteItem(req, res, next, null, itemName, "results"); }); -app.all("*", function (req, res, next) { - var err = new Error("Route not supported. Please check your backend URL"); - next(err); +/** +* Middleware to handle not supported routes + */ +app.use((req, res, next) => { + const error = new Error("Not found"); + error.status = 404; + next(error); }); -app.use(function (error, req, res, next) { +// error handler middleware +app.use((error, req, res, next) => { console.log(error); - res.status(500).send("Oops!! An error occurred. Check the logs for more info. This is a message from your BACKEND"); + if (res.headersSent) { + return next(error); + } + res.status(error.status || 500).send({ + error: { + status: error.status || 500, + message: error.message || "Internal Server error", + }, + }); }); app.listen(port, () => console.log(`App listening on port ${port}!`)); diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..43c405e --- /dev/null +++ b/deploy.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# declare an array called array and define 3 vales +declare -a folders=( "frontend" "backend" "jobs" ) +for folder in "${folders[@]}" +do + echo $folder + cd $folder + if [ $folder == "jobs" ] + then + docker build . -t ibmcom/backend-job && docker push ibmcom/backend-job + else + docker build . -t ibmcom/$folder && docker push ibmcom/$folder + fi + cd .. +done diff --git a/frontend/app.js b/frontend/app.js index 340af02..8467580 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -27,12 +27,34 @@ app.get('/', function(req, res) { res.sendFile(__dirname + "/public/index.html"); } }); + +app.get('/items', async(req, res) => { + req.pipe( + await request.get( + { + url: backendURL+"/items?prefix=images", + agentOptions: { + rejectUnauthorized: false + } + }, + function(error, resp, body) { + if (error) { + res.status(400).send(error.message); + } + else{ + //console.log(body); + res.send({ data: body }); + } + } + ) + ); +}); /* * Upload an image for Image classification */ -app.post("/uploadimage", function(req, res) { +app.post("/uploadimage", async(req, res) => { req.pipe( - request.post( + await request.post( { url: backendURL+"/images", gzip: true, @@ -42,6 +64,7 @@ app.post("/uploadimage", function(req, res) { }, function(error, resp, body) { if (error) { + console.log(error); res.status(400).send(error.message); } else{ @@ -54,9 +77,9 @@ app.post("/uploadimage", function(req, res) { }); -app.post("/classifyimage", function(req, res) { - req.pipe( - request.post( +app.post("/classifyimage", async(req, res) => { + req.pipe( + await request.post( { url: backendURL+"/results", agentOptions: { @@ -65,6 +88,7 @@ app.post("/classifyimage", function(req, res) { }, function(error, resp, body) { if (error) { + console.log(error); res.status(400).send(error.message); } else{ @@ -77,6 +101,31 @@ app.post("/classifyimage", function(req, res) { }); +app.delete("/image", async (req, res) => { + var itemName = req.query.filename; + req.pipe( + await request.delete( + { + url: backendURL+"/item?filename="+itemName, + agentOptions: { + rejectUnauthorized: false + } + }, + function(error, resp, body) { + if (error) { + console.log(error); + res.status(400).send(error.message); + } + else{ + //console.log(body); + res.send({ data: body }); + } + } + ) + ); + +}); + app.use(function(error, req, res, next) { res.status(500).send(error.message); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c78cb80..d9a0f31 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,5 +1,5 @@ { - "name": "coligo-object-detection", + "name": "codeengine-image-classification", "version": "1.0.0", "lockfileVersion": 1, "requires": true, diff --git a/frontend/package.json b/frontend/package.json index 8c4c393..b639573 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { - "name": "coligo-object-detection", + "name": "codeengine-image-classification", "version": "1.0.0", - "description": "Image Classification with Coligo and Watson Visual Recognition", + "description": "Image Classification with Code Engine and Watson Visual Recognition", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/frontend/public/501.html b/frontend/public/501.html index 25e506a..8f2341a 100644 --- a/frontend/public/501.html +++ b/frontend/public/501.html @@ -4,7 +4,7 @@ - Image classification with Coligo and Watson Visual Recognition + Image classification with Code Engine