first commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
WRENCHJOB_PORT=3000
|
||||||
|
WRENCHJOB_POSTGRE_URL='postgresql://wrenchboard:wrenchboard@10.20.30.60:5432/wrenchboard'
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
FROM node:erbium
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
RUN mkdir -p /usr/src/app
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Install app dependencies
|
||||||
|
COPY package.json /usr/src/app/
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . /usr/src/app
|
||||||
|
|
||||||
|
CMD [ "npm", "start" ]
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const properties = require('../package.json')
|
||||||
|
const transaction = require('../service/transaction');
|
||||||
|
const transfer = require('../service/transfer');
|
||||||
|
const account = require('../service/account');
|
||||||
|
|
||||||
|
var controllers = {
|
||||||
|
about: function(req, res) {
|
||||||
|
var aboutInfo = {
|
||||||
|
name: properties.name,
|
||||||
|
version: properties.version
|
||||||
|
}
|
||||||
|
res.json(aboutInfo);
|
||||||
|
},
|
||||||
|
postTransfer: function(req, res) {
|
||||||
|
transfer.create(req, res, function(err, dist) {
|
||||||
|
if (err) {
|
||||||
|
res.send(err);
|
||||||
|
}
|
||||||
|
res.json(dist);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getStatus: function(req, res) {
|
||||||
|
transfer.get(req, res, function(err, dist) {
|
||||||
|
if (err) {
|
||||||
|
res.send(err);
|
||||||
|
}
|
||||||
|
res.json(dist);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
verifyTransaction: function(req, res) {
|
||||||
|
transaction.verify(req, res, function(err, dist) {
|
||||||
|
if (err) {
|
||||||
|
res.send(err);
|
||||||
|
}
|
||||||
|
res.json(dist);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
verifyAccount: function(req, res) {
|
||||||
|
account.verify(req, res, function(err, dist) {
|
||||||
|
if (err) {
|
||||||
|
res.send(err);
|
||||||
|
}
|
||||||
|
res.json(dist);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = controllers;
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const controller = require('./controller');
|
||||||
|
|
||||||
|
module.exports = function(app) {
|
||||||
|
app.route('/about')
|
||||||
|
.get(controller.about);
|
||||||
|
app.route('/create')
|
||||||
|
.post(controller.postTransfer);
|
||||||
|
app.route('/status/:id')
|
||||||
|
.get(controller.getStatus);
|
||||||
|
app.route('/verifyTransaction/:id')
|
||||||
|
.get(controller.verifyTransaction);
|
||||||
|
app.route('/verify')
|
||||||
|
.post(controller.verifyAccount);
|
||||||
|
};
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Configures the database connection
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { Pool, Client } = require('pg');
|
||||||
|
const url = require('url');
|
||||||
|
const logger = require('./logger');
|
||||||
|
//const connectionString = 'postgresql://wrenchboard:wrenchboard@10.10.10.23:5432/wrenchboard';
|
||||||
|
const connectionString = process.env.POSTGRE_URL.replace(/'/g, '');
|
||||||
|
const params = url.parse(connectionString);
|
||||||
|
const auth = params.auth.split(':');
|
||||||
|
const config = {
|
||||||
|
user: auth[0],
|
||||||
|
password: auth[1],
|
||||||
|
host: params.hostname,
|
||||||
|
port: params.port,
|
||||||
|
database: params.pathname.split('/')[1],
|
||||||
|
ssl: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const postgres = new Pool(config);
|
||||||
|
|
||||||
|
postgres.on('connect', client => {
|
||||||
|
logger.info('Connected to Database');
|
||||||
|
});
|
||||||
|
|
||||||
|
postgres.on('acquire', client => {
|
||||||
|
logger.info('Client is checked out from the DB connection pool');
|
||||||
|
});
|
||||||
|
|
||||||
|
postgres.on('remove', client => {
|
||||||
|
logger.info('Client is closed & removed from the DB connection pool');
|
||||||
|
});
|
||||||
|
|
||||||
|
postgres.on('error', (err, client) => {
|
||||||
|
logger.error(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Connect to PostgreSQL
|
||||||
|
postgres.connect((err, client, release) => {
|
||||||
|
logger.info(connectionString);
|
||||||
|
if (err) {
|
||||||
|
logger.error('Error acquiring client', err.stack);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
client.query('SELECT NOW()', (err, result) => {
|
||||||
|
release();
|
||||||
|
if (err) {
|
||||||
|
logger.error('Error executing query', err.stack);
|
||||||
|
return nul;
|
||||||
|
}
|
||||||
|
logger.info(result.rows);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = postgres;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Set up the app
|
||||||
|
*/
|
||||||
|
var _ = require('underscore');
|
||||||
|
var db = require('./db');
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
db();
|
||||||
|
};
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Defines logger for the microservice
|
||||||
|
*/
|
||||||
|
|
||||||
|
var winston = require('winston');
|
||||||
|
var Papertrail = require('winston-papertrail').Papertrail;
|
||||||
|
var serviceIdentifier = require('../package.json').name;
|
||||||
|
|
||||||
|
var host = process.env.LOG_SERVICE_HOST;
|
||||||
|
var port = process.env.LOG_SERVICE_PORT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to provide logging facilities for the microservice
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Logger = function () {
|
||||||
|
|
||||||
|
// Try to route logs to third party service
|
||||||
|
if (host && port) {
|
||||||
|
// Define our transport
|
||||||
|
var transport = new Papertrail({
|
||||||
|
levels: {
|
||||||
|
debug: 0,
|
||||||
|
info: 1,
|
||||||
|
error: 3
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
debug: 'blue',
|
||||||
|
info: 'green',
|
||||||
|
error: 'red'
|
||||||
|
},
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
json: true,
|
||||||
|
colorize: true,
|
||||||
|
logFormat: (level, message) => {
|
||||||
|
return `[${serviceIdentifier}] ${level} : ${message}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle exceptions
|
||||||
|
transport.exceptionsLevel = 'error';
|
||||||
|
winston.handleExceptions(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.info = winston.info;
|
||||||
|
this.error = winston.error;
|
||||||
|
this.debug = winston.debug;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = new Logger();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Logger
|
||||||
|
* @property {function} info - Log in level `info`
|
||||||
|
* @property {function} error - Log in level `error`
|
||||||
|
* @property {function} debug - Log in level `debug`
|
||||||
|
*/
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
wrenchboard-jobs-micro:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
# image: registry.chiefsoft.net/flutterwave-transfer-micro:latest
|
||||||
|
volumes:
|
||||||
|
- ./:/app
|
||||||
|
- '/app/node_modules'
|
||||||
|
ports:
|
||||||
|
- 9086:3000
|
||||||
|
environment:
|
||||||
|
- PORT=${WRENCHJOB_PORT}
|
||||||
|
- WRENCHJOB_POSTGRE_URL=${FLUTTERWAVE_POSTGRE_URL}
|
||||||
|
volumes:
|
||||||
|
src:
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "wrenhboard-job-micro",
|
||||||
|
"version": "0.0.4",
|
||||||
|
"description": "A microservice to handle wrenchboard job listing",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"generate-docs": "jsdoc ./api/*.js ./api/**/*.js ./app/*.js ./app/**/*.js ./service/*.js ./service/**/*.js -d docs",
|
||||||
|
"lint": "eslint ./ --ext .js",
|
||||||
|
"start": "node server.js",
|
||||||
|
"start:dev": "nodemon server.js",
|
||||||
|
"test": "npm run lint"
|
||||||
|
},
|
||||||
|
"author": "WrenchBoard Support <support@wrenchboard.com>",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.24.0",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"openapi-types": "^10.0.0",
|
||||||
|
"pg": "8.7.1",
|
||||||
|
"pg-pool": "^3.5.1",
|
||||||
|
"request": "^2.88.2",
|
||||||
|
"swagger-autogen": "^2.17.2",
|
||||||
|
"swagger-jsdoc": "^6.1.0",
|
||||||
|
"swagger-ui-express": "^4.3.0",
|
||||||
|
"underscore": "^1.8.3",
|
||||||
|
"url": "^0.11.0",
|
||||||
|
"winston": "^2.3.1",
|
||||||
|
"winston-papertrail": "^1.0.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^1.10.3",
|
||||||
|
"jsdoc": "^3.4.3",
|
||||||
|
"nodemon": "^1.11.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const logger = require('./app/logger');
|
||||||
|
const port = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const routes = require('./api/routes');
|
||||||
|
routes(app);
|
||||||
|
|
||||||
|
app.listen(port, "0.0.0.0", function() {
|
||||||
|
logger.info('***** Server started on port: ' + port + ' *****');
|
||||||
|
});
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
const request = require('request');
|
||||||
|
const db = require('../app/db')
|
||||||
|
const logger = require('../app/logger');
|
||||||
|
|
||||||
|
// https://developer.flutterwave.com/reference/create-a-transfer
|
||||||
|
const flutterwaveConfigBuffer = new Buffer(process.env.FLUTTERWAVE_API, 'base64');
|
||||||
|
const flutterwaveConfig = JSON.parse(flutterwaveConfigBuffer.toString('ascii'));
|
||||||
|
|
||||||
|
var transfer = {
|
||||||
|
create: function(req, res, next) {
|
||||||
|
var url = flutterwaveConfig.BaseApiUrl + '/v3/transfers';
|
||||||
|
var headersOpt = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": "Bearer " + flutterwaveConfig.SecretKey,
|
||||||
|
};
|
||||||
|
var data = {
|
||||||
|
"account_bank": req.body.account_bank,
|
||||||
|
"account_number": req.body.account_number,
|
||||||
|
"amount": req.body.amount,
|
||||||
|
"narration": req.body.narration,
|
||||||
|
"currency": req.body.currency,
|
||||||
|
"reference": req.body.reference,
|
||||||
|
"debit_currency": req.body.debit_currency
|
||||||
|
};
|
||||||
|
var options = {
|
||||||
|
headers: headersOpt
|
||||||
|
};
|
||||||
|
logger.info(url);
|
||||||
|
logger.info(options);
|
||||||
|
logger.info(data);
|
||||||
|
/*
|
||||||
|
CREATE TABLE flutterwave (
|
||||||
|
id bigserial,
|
||||||
|
money_transfer_id int NOT NULL REFERENCES money_transfer(id),
|
||||||
|
request json NOT NULL,
|
||||||
|
response json NOT NULL,
|
||||||
|
created timestamp DEFAULT NOW(),
|
||||||
|
PRIMARY KEY(id)
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
// Save request in the DB
|
||||||
|
var queryText = 'INSERT INTO flutterwave(money_transfer_id, request) VALUES($1, $2) RETURNING id'
|
||||||
|
db.query(queryText, [req.body.money_transfer_id, data], function(dbErr, result) {
|
||||||
|
if (dbErr) {
|
||||||
|
logger.info('Failed to save request in the DB for money_transfer_id =', req.body.money_transfer_id);
|
||||||
|
logger.info(dbErr);
|
||||||
|
res.send({
|
||||||
|
status: "failure",
|
||||||
|
message: "Failed to save request in the DB!",
|
||||||
|
details: dbErr.message,
|
||||||
|
code: null,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var flutterwave_id = result.rows[0].id;
|
||||||
|
logger.info('Request saved int the DB for money_transfer_id =', flutterwave_id);
|
||||||
|
logger.info(result.rows);
|
||||||
|
// https://blog.logrocket.com/5-ways-to-make-http-requests-in-node-js/
|
||||||
|
// https://stackoverflow.com/questions/53846709/how-do-i-use-axios-within-expressjs
|
||||||
|
axios.post(url, data, options)
|
||||||
|
.then(response => {
|
||||||
|
//res.json(response);
|
||||||
|
logger.info('-------------------------------');
|
||||||
|
logger.info(response.data);
|
||||||
|
logger.info('-------------------------------');
|
||||||
|
var json = {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
data: response.data
|
||||||
|
};
|
||||||
|
queryText = 'UPDATE flutterwave SET response=$1 WHERE id=$2';
|
||||||
|
db.query(queryText, [json, flutterwave_id], function(dbErr, result) {
|
||||||
|
if (dbErr) {
|
||||||
|
logger.info('Failed to save response in the DB for money_transfer_id =', flutterwave_id);
|
||||||
|
logger.info(dbErr);
|
||||||
|
} else {
|
||||||
|
logger.info('Response saved int the DB for money_transfer_id =', flutterwave_id);
|
||||||
|
logger.info('Updated row count:', result.rowCount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.json(json);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
// https://stackoverflow.com/questions/45017822/catching-error-body-using-axios-post
|
||||||
|
var errorMessage = err.message;
|
||||||
|
logger.error(err.message);
|
||||||
|
//logger.error(err.stack);
|
||||||
|
//logger.error(err.response.data);
|
||||||
|
//logger.error(err.response.headers);
|
||||||
|
if ( err.request ) {
|
||||||
|
console.log(err.request);
|
||||||
|
// err.request.response
|
||||||
|
errorMessage = err.request;
|
||||||
|
}
|
||||||
|
if( err.response ) {
|
||||||
|
console.log(err.response.data); // => the response payload
|
||||||
|
errorMessage = err.response.data;
|
||||||
|
}
|
||||||
|
queryText = 'UPDATE flutterwave SET response=$1 WHERE id=$2';
|
||||||
|
db.query(queryText, [errorMessage, flutterwave_id], function(dbErr, result) {
|
||||||
|
if (dbErr) {
|
||||||
|
logger.info('Failed to save error response in the DB for money_transfer_id =', flutterwave_id);
|
||||||
|
logger.info(dbErr);
|
||||||
|
} else {
|
||||||
|
logger.info('Error response saved int the DB for money_transfer_id =', flutterwave_id);
|
||||||
|
logger.info('Updated row count:', result.rowCount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.send({
|
||||||
|
status: "failure",
|
||||||
|
message: "Invalid response",
|
||||||
|
details: errorMessage,
|
||||||
|
code: typeof err.response !== 'undefined' ? err.response.status : null,
|
||||||
|
data: typeof err.response !== 'undefined' ? err.response.data : null
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Check DB time stamp (integrity check)
|
||||||
|
db.query('SELECT NOW()', (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
logger.info('time:', res.rows[0])
|
||||||
|
});
|
||||||
|
},
|
||||||
|
get: function(req, res, next) {
|
||||||
|
var url = flutterwaveConfig.BaseApiUrl + '/v3/transfers/' + req.params.id;
|
||||||
|
var headersOpt = {
|
||||||
|
"Authorization": "Bearer " + flutterwaveConfig.SecretKey,
|
||||||
|
};
|
||||||
|
var options = {
|
||||||
|
headers: headersOpt
|
||||||
|
};
|
||||||
|
logger.info(url);
|
||||||
|
axios.get(url, options)
|
||||||
|
.then(response => {
|
||||||
|
//res.json(response);
|
||||||
|
logger.info('-------------------------------');
|
||||||
|
logger.info(response.data);
|
||||||
|
logger.info('-------------------------------');
|
||||||
|
var json = {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
data: response.data
|
||||||
|
};
|
||||||
|
res.json(json);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error(err.message);
|
||||||
|
//logger.error(err.stack);
|
||||||
|
logger.error(err.response.data);
|
||||||
|
//logger.error(err.response.headers);
|
||||||
|
res.send({
|
||||||
|
status: "failure",
|
||||||
|
message: "Invalid response",
|
||||||
|
details: err.message,
|
||||||
|
code: err.response.status,
|
||||||
|
data: err.response.data
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// TODO: Do we save the response in the DB? I assume: YES
|
||||||
|
db.query('SELECT NOW()', (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
logger.info('time:', res.rows[0])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = transfer;
|
||||||
Reference in New Issue
Block a user