-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
shivam.tay
committed
Oct 7, 2020
0 parents
commit 222b0c6
Showing
28 changed files
with
2,167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Created by .ignore support plugin (hsz.mobi) | ||
node_modules/ | ||
.idea/ | ||
npm-debug.log* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
#Image Upload Service | ||
This Image Upload Service uploads a given image of type jpeg/png with a description to AWS S3 and stores the image metadata to AWS MySQL RDS. The service is extendable to support Db migration as well. It handles all the edge cases and failures and displays user friendly message to the end user. | ||
The service also handles logical inconsistency where it first tries to upload the image to S3, if it is successfully uploaded then it tries to insert the data into RDS. If RDS operation fails, then the uploaded image is deleted from S3 and user is notified that the upload was unsuccessful. | ||
The backend is written in Javascript. | ||
|
||
##System Dependencies | ||
Ensure following dependencies are present before installing the application. | ||
1. ```AWS S3``` bucket for storing the images. Ensure that ```access key``` provided to the application has the permission to do ```PutObject``` and ```DeleteObject``` commands on the bucket. | ||
2. ```AWS Mysql RDS``` version 5.x or later. Ensure that username and password combination provided to the application has access to connect to the RDS and do create, drop & insert query on it. | ||
|
||
##Installation of service | ||
|
||
###Local environment | ||
1. Make sure ```node 8.x or higher``` and ```npm 6.x``` is installed. | ||
2. Find ```deployConfig.js``` file inside ```config``` folder and put the bucket name and RDS host in ```dev``` config. Rest details need not be changed. | ||
``` | ||
awsBucketName: | ||
mysqlDbConfig: { | ||
host: | ||
port: | ||
} | ||
``` | ||
3. Find ```aws-credentials``` file in the project directory and put the following fields in under ```image-upload-service-dev``` tag. The fields are self-explanatory. | ||
``` | ||
aws_access_key_id = | ||
aws_secret_access_key = | ||
rds_image_db_user = | ||
rds_image_db_password = | ||
``` | ||
4. Checkout the Project directory in terminal and run ```npm install ```. | ||
5. Run ```npm start``` after the npm packages are installed successfully. | ||
6. The server will start and create the required database and table on its own in the provided ```AWS RDS```. | ||
7. Open ```127.0.0.1``` in browser (The Latest Chrome version), and the application will be ready to use. | ||
8. Application logs can be found inside ```logs``` folder. | ||
|
||
###Production environment | ||
1. Make sure ```node 8.x or higher``` and ```npm 6.x``` is installed. | ||
2. Find ```deployConfig.js``` file inside ```config``` folder and put the bucket name and RDS host in ```prod``` config. Rest details need not be changed. | ||
``` | ||
awsBucketName: | ||
mysqlDbConfig: { | ||
host: | ||
port: | ||
} | ||
``` | ||
3. Find ```aws-credentials``` file in the project directory and put the following fields in under ```image-upload-service-prod``` tag. The fields are self-explanatory. | ||
``` | ||
aws_access_key_id = | ||
aws_secret_access_key = | ||
rds_image_db_user = | ||
rds_image_db_password = | ||
``` | ||
4. Checkout the Project directory in terminal and run ```npm install ```. | ||
5. Export env variable using command ```export NODE_ENV=prod```. | ||
6. Run ```npm start``` after the npm packages are installed successfully. | ||
7. The server will start and create the required database and table on its own in the provided ```AWS RDS```. | ||
8. Application logs can be found inside ```logs``` folder. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
var express = require('express'); | ||
var path = require('path'); | ||
var cookieParser = require('cookie-parser'); | ||
var logger = require("./logger"); | ||
var expressWinston = require('express-winston'); | ||
var winston = require('winston'); | ||
var projectEnv = require('./config/projectEnv').projectEnv; | ||
var accessLogsfile = projectEnv.accessLogs.filename; | ||
|
||
var indexRouter = require('./routes/index'); | ||
|
||
var app = express(); | ||
|
||
var allowCrossDomain = function(req, res, next) { | ||
res.header('Access-Control-Allow-Origin', '*'); | ||
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); | ||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); | ||
next(); | ||
}; | ||
app.use(allowCrossDomain); | ||
|
||
app.use(expressWinston.logger({ | ||
transports: [ | ||
new winston.transports.DailyRotateFile({ | ||
name : 'tps-access-file', | ||
datePattern: '.yyyy-MM-dd', | ||
filename: accessLogsfile, | ||
handleExceptions: true, | ||
json: true, | ||
colorize: false | ||
}) | ||
], | ||
meta: true, // optional: control whether you want to log the meta data about the request (default to true) | ||
expressFormat: true, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true | ||
colorStatus: true // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true | ||
})); | ||
|
||
// view engine setup | ||
app.set('views', path.join(__dirname, 'views')); | ||
|
||
app.use(express.json({ limit: '1mb'})); | ||
app.use(express.urlencoded({ extended: false, limit: '1mb'})); | ||
app.use(cookieParser()); | ||
app.use(express.static(path.join(__dirname, 'public'))); | ||
|
||
app.use('/', indexRouter); | ||
|
||
// error handler | ||
app.use(function(err, req, res, next) { | ||
var errorObj={}; | ||
errorObj['err_name'] = "APP_FAILURE"; | ||
errorObj['err_stk'] = err.stack; | ||
logger.error(JSON.stringify(errorObj)); | ||
if(err.message === 'request entity too large') { | ||
res.status(400).send(err); | ||
} else { | ||
res.status(500).send(err); | ||
res.end(); | ||
} | ||
}); | ||
|
||
module.exports = app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#aws credentials for dev environment | ||
[image-upload-service-dev] | ||
aws_access_key_id = | ||
aws_secret_access_key = | ||
rds_image_db_user = | ||
rds_image_db_password = | ||
|
||
#aws credentials for prod environment | ||
[image-upload-service-prod] | ||
aws_access_key_id = | ||
aws_secret_access_key = | ||
rds_image_db_user = | ||
rds_image_db_password = |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
#!/usr/bin/env node | ||
|
||
/** | ||
* Module dependencies. | ||
*/ | ||
|
||
var Q = require('q'); | ||
var app = require('../app'); | ||
var debug = require('debug')('alerts-framework:server'); | ||
var http = require('http'); | ||
var projectEnv = require('../config/projectEnv').projectEnv; | ||
const logger = require('../logger'); | ||
|
||
/** | ||
* Get port from environment and store in Express. | ||
*/ | ||
|
||
var port = normalizePort(process.env.PORT || projectEnv.httpPort); | ||
app.set('port', port); | ||
|
||
/** | ||
* Create HTTP server. | ||
*/ | ||
|
||
var server = http.createServer(app); | ||
var defer = Q.defer(); | ||
|
||
var startServer = function () { | ||
server.listen(port); | ||
server.on('error', onError); | ||
server.on('listening', onListening); | ||
return defer.promise; | ||
}; | ||
|
||
/** | ||
* Normalize a port into a number, string, or false. | ||
*/ | ||
|
||
function normalizePort(val) { | ||
var port = parseInt(val, 10); | ||
|
||
if (isNaN(port)) { | ||
// named pipe | ||
return val; | ||
} | ||
|
||
if (port >= 0) { | ||
// port number | ||
return port; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Event listener for HTTP server "error" event. | ||
*/ | ||
|
||
function onError(error) { | ||
if (error.syscall !== 'listen') { | ||
throw error; | ||
} | ||
|
||
var bind = typeof port === 'string' | ||
? 'Pipe ' + port | ||
: 'Port ' + port; | ||
|
||
// handle specific listen errors with friendly messages | ||
switch (error.code) { | ||
case 'EACCES': | ||
logger.error(bind + ' requires elevated privileges'); | ||
process.exit(1); | ||
break; | ||
case 'EADDRINUSE': | ||
logger.error(bind + ' is already in use'); | ||
process.exit(1); | ||
break; | ||
default: | ||
throw error; | ||
} | ||
} | ||
|
||
/** | ||
* Event listener for HTTP server "listening" event. | ||
*/ | ||
|
||
function onListening() { | ||
var addr = server.address(); | ||
var bind = typeof addr === 'string' | ||
? 'pipe ' + addr | ||
: 'port ' + addr.port; | ||
logger.info('Listening on ' + bind); | ||
defer.resolve(); | ||
} | ||
|
||
exports.startServer = startServer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
exports.envConfig = { | ||
"dev": { | ||
logPath:'./logs/', | ||
logs: { | ||
transports: [ | ||
{name: 'image-upload-service-error-file', level: 'error', filename: './logs/image-upload-service-error-logs.log'}, | ||
{name: 'image-upload-service-info-file', level: 'info', filename: './logs/image-upload-service-info-logs.log'} | ||
], | ||
console: true | ||
}, | ||
// Bucket name for dev environment | ||
awsBucketName: '', | ||
// AWS mysql RDS details for dev environment | ||
mysqlDbConfig: { | ||
host: '', | ||
port: 3306, | ||
database: 'imagesmetadatadbdev', | ||
tableName: 'image_metadata', | ||
}, | ||
awsCredProfile: 'image-upload-service-dev', | ||
awsCredFilePath: __dirname.replace(/config$/,'') + 'aws-credentials', | ||
accessLogs: {filename: './logs/image-upload-service-access-logs.log'}, | ||
httpPort: 80 | ||
}, | ||
|
||
"prod": { | ||
logPath:'./logs/', | ||
logs: { | ||
transports: [ | ||
{name: 'image-upload-service-error-file', level: 'error', filename: './logs/image-upload-service-error-logs.log'}, | ||
{name: 'image-upload-service-info-file', level: 'info', filename: './logs/image-upload-service-info-logs.log'} | ||
], | ||
console: true | ||
}, | ||
// Bucket name for production setup | ||
awsBucketName: '', | ||
// AWS mysql RDS details for production | ||
mysqlDbConfig: { | ||
host: '', | ||
port: 3306, | ||
database: 'imagesmetadatadbprod', | ||
tableName: 'image_metadata', | ||
}, | ||
awsCredProfile: 'image-upload-service-prod', | ||
awsCredFilePath: __dirname.replace(/config$/,'') + 'aws-credentials', | ||
accessLogs: {filename: './logs/image-upload-service-access-logs.log'}, | ||
httpPort: 80 | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
var config = require('./deployConfig').envConfig; | ||
var env = process.env.NODE_ENV; | ||
|
||
if(env == undefined){ | ||
env = "dev" | ||
} | ||
|
||
console.log("env :" + env); | ||
var projectEnv = config[env]; | ||
|
||
exports.projectEnv = projectEnv; | ||
exports.env = env; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<module type="WEB_MODULE" version="4"> | ||
<component name="NewModuleRootManager" inherit-compiler-output="true"> | ||
<exclude-output /> | ||
<content url="file://$MODULE_DIR$" /> | ||
<orderEntry type="sourceFolder" forTests="false" /> | ||
</component> | ||
</module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
const async = require('async'); | ||
const Q = require('q'); | ||
const logger = require('../logger'); | ||
const server = require('../bin/www'); | ||
const initDbConnection = require('../persistence/dbConnection'); | ||
const initMysqlDbAndTables = require('./initMysqlDbAndTables'); | ||
|
||
const initFunctions = [ | ||
{promiseMethod: initDbConnection}, | ||
{promiseMethod: initMysqlDbAndTables}, | ||
{promiseMethod: server.startServer} | ||
]; | ||
|
||
const executePromises = function (params) { | ||
const defer = Q.defer(); | ||
try { | ||
async.eachSeries(params.promiseArray, function (currentPromiseObj, callback) { | ||
try { | ||
params.logger.info({'msg':'inprocess',promiseMethod:currentPromiseObj.promiseMethod.name}); | ||
if (currentPromiseObj.sync) { | ||
currentPromiseObj.promiseMethod(currentPromiseObj.params); | ||
params.logger.info({'msg':"success",promiseMethod:currentPromiseObj.promiseMethod.name}); | ||
callback(); | ||
} | ||
else { | ||
currentPromiseObj.promiseMethod(currentPromiseObj.params).then(function () { | ||
params.logger.info({'msg':"success",promiseMethod:currentPromiseObj.promiseMethod.name}); | ||
callback(); | ||
}, function (err) { | ||
params.logger.error({err_name:'PROMISE_METHOD_ERROR',promiseMethod:currentPromiseObj.promiseMethod.name,err_stk:err.stack}); | ||
if(currentPromiseObj.skipErrors) | ||
callback(); | ||
else | ||
callback(err); | ||
}) | ||
} | ||
} | ||
catch (ex) { | ||
params.logger.error({err_name:'PROMISE_METHOD_EXCEPTION',promiseMethod:currentPromiseObj.promiseMethod.name,err_stk:ex.stack}); | ||
if(currentPromiseObj.skipErrors) | ||
callback(); | ||
else | ||
callback(ex); | ||
} | ||
}, function (err) { | ||
if (err) { | ||
params.logger.error({err_name:'PROMISE_ARRAY_ERROR',err_stk:err.stack}); | ||
defer.reject(err); | ||
} | ||
else | ||
defer.resolve(); | ||
}) | ||
} | ||
catch (ex) { | ||
params.logger.error({err_name:'PROMISE_ARRAY_EXCEPTION',err_stk:ex.stack}); | ||
defer.reject(ex); | ||
} | ||
return defer.promise; | ||
}; | ||
|
||
var init = function () { | ||
try { | ||
executePromises({ | ||
promiseArray: initFunctions, | ||
sequence: true, | ||
logger: logger | ||
}).then(function () { | ||
logger.info({msg: 'INIT_SUCCESS'}); | ||
logger.info({msg: 'Application running on http://127.0.0.1/'}); | ||
}, function (err) { | ||
logger.error({err_name: 'INIT_FAILURE', err_stk: err.stack}); | ||
}); | ||
} | ||
catch (ex) { | ||
logger.error({err_name: 'INIT_FAILURE', err_stk: ex.stack}); | ||
} | ||
}; | ||
|
||
init(); |
Oops, something went wrong.