From 00c41870811c3644f732b20e37cdf60cf9e482d7 Mon Sep 17 00:00:00 2001 From: Rafael Taranto Date: Fri, 5 Jul 2019 17:17:59 +0100 Subject: [PATCH] Front facing camera (#289) * Front facing camera * Adding in configuration and update script --- bin/lamassu-configure-frontcamera | 12 ++++++ bin/lamassu-update | 3 ++ lamassu-admin-elm/src/Customer/View.elm | 13 ++++++ lamassu-default.json | 3 +- lamassu-remote-install/install | 2 + lib/admin/admin-server.js | 6 +++ lib/customers.js | 54 +++++++++++++++++++++++- lib/routes.js | 1 + package.json | 1 + public/elm.js | 55 ++++++++++++++++++++++--- 10 files changed, 142 insertions(+), 8 deletions(-) create mode 100755 bin/lamassu-configure-frontcamera diff --git a/bin/lamassu-configure-frontcamera b/bin/lamassu-configure-frontcamera new file mode 100755 index 00000000..5a6d4d06 --- /dev/null +++ b/bin/lamassu-configure-frontcamera @@ -0,0 +1,12 @@ +#!/usr/bin/env node + +'use strict' + +const fs = require('fs') +const options = require('../lib/options-loader')() + +if (!options.opts.frontCameraDir) { + options.opts.frontCameraDir = '/opt/lamassu-server/frontcamera' + + fs.writeFileSync(options.path, JSON.stringify(options.opts, null, '\t'), 'utf8') +} diff --git a/bin/lamassu-update b/bin/lamassu-update index 4025122d..b415a7ae 100755 --- a/bin/lamassu-update +++ b/bin/lamassu-update @@ -71,6 +71,9 @@ lamassu-migrate-config >> ${LOG_FILE} 2>&1 decho "update to mnemonic" lamassu-update-to-mnemonic --prod >> ${LOG_FILE} 2>&1 +decho "update configure frontcamera" +lamassu-configure-frontcamera >> ${LOG_FILE} 2>&1 + decho "update ofac sources" lamassu-ofac-update-sources >> ${LOG_FILE} 2>&1 diff --git a/lamassu-admin-elm/src/Customer/View.elm b/lamassu-admin-elm/src/Customer/View.elm index d1370051..d2b1e4c2 100644 --- a/lamassu-admin-elm/src/Customer/View.elm +++ b/lamassu-admin-elm/src/Customer/View.elm @@ -178,6 +178,19 @@ customerView customer = , alt "N/A" ] [] ] + , h2 [] [ text "Front Facing Camera Photo" ] + , case customer.frontCameraPath of + Nothing -> + text "N/A" + + Just frontCameraPath -> + div [] + [ img + [ src ("/front-camera-photo/" ++ frontCameraPath) + , height 200 + , alt "N/A" + ] [] + ] ] diff --git a/lamassu-default.json b/lamassu-default.json index f8400b72..19943309 100644 --- a/lamassu-default.json +++ b/lamassu-default.json @@ -13,5 +13,6 @@ "strike": { "baseUrl": "https://api.strike.acinq.co/api/" }, - "idPhotoCardDir": "/opt/lamassu-server/idphotocard" + "idPhotoCardDir": "/opt/lamassu-server/idphotocard", + "frontCameraDir": "/opt/lamassu-server/frontcamera" } diff --git a/lamassu-remote-install/install b/lamassu-remote-install/install index 32da00df..563f8e21 100755 --- a/lamassu-remote-install/install +++ b/lamassu-remote-install/install @@ -18,6 +18,7 @@ BACKUP_DIR=/var/backups/postgresql BLOCKCHAIN_DIR=/mnt/blockchains OFAC_DATA_DIR=/var/lamassu/ofac ID_PHOTO_CARD_DIR=/opt/lamassu-server/idphotocard +FRONTCAMERA_DIR=/opt/lamassu-server/frontcamera # Look into http://unix.stackexchange.com/questions/140734/configure-localtime-dpkg-reconfigure-tzdata @@ -181,6 +182,7 @@ cat < $CONFIG_DIR/lamassu.json "blockchainDir": "$BLOCKCHAIN_DIR", "ofacDataDir": "$OFAC_DATA_DIR", "idPhotoCardDir": "$ID_PHOTO_CARD_DIR", + "frontCameraDir": "$FRONTCAMERA_DIR", "strike": { "baseUrl": "https://api.strike.acinq.co/api/" }, diff --git a/lib/admin/admin-server.js b/lib/admin/admin-server.js index 06a0d0b5..0c8a0137 100644 --- a/lib/admin/admin-server.js +++ b/lib/admin/admin-server.js @@ -38,6 +38,7 @@ const supportServer = require('./admin-support') const NEVER = new Date(Date.now() + 100 * T.years) const REAUTHENTICATE_INTERVAL = T.minute const idPhotoCardBasedir = _.get('idPhotoCardDir', options) +const frontCameraBasedir = _.get('frontCameraDir', options) const devMode = argv.dev @@ -255,7 +256,12 @@ if (!fs.existsSync(idPhotoCardBasedir)) { makeDir.sync(idPhotoCardBasedir) } +if (!fs.existsSync(frontCameraBasedir)) { + makeDir.sync(frontCameraBasedir) +} + app.use('/id-card-photo', serveStatic(idPhotoCardBasedir, {index: false})) +app.use('/front-camera-photo', serveStatic(frontCameraBasedir, {index: false})) function register (req, res, next) { const otp = req.query.otp diff --git a/lib/customers.js b/lib/customers.js index 93ccd5aa..51478d11 100644 --- a/lib/customers.js +++ b/lib/customers.js @@ -8,7 +8,6 @@ const fs = require('fs') const util = require('util') const moment = require('moment') - const db = require('./db') const BN = require('./bn') const anonymous = require('../lib/constants').anonymousCustomer @@ -19,6 +18,7 @@ const writeFile = util.promisify(fs.writeFile) const NUM_RESULTS = 1000 const idPhotoCardBasedir = _.get('idPhotoCardDir', options) +const frontCameraBaseDir = _.get('frontCameraDir', options) /** * Add new customer @@ -432,4 +432,54 @@ function updatePhotoCard (id, patch) { }) } -module.exports = {add, get, batch, getById, update, updatePhotoCard} +function updateFrontCamera (id, patch) { + return Promise.resolve(patch) + .then(patch => { + // Base64 encoded image /9j/4AAQSkZJRgABAQAAAQ.. + const imageData = _.get('frontCameraData', patch) + + if (_.isEmpty(imageData)) { + return patch + } + + // remove idCardPhotoData from the update record + const newPatch = _.omit('frontCameraData', patch) + + // decode the base64 string to binary data + const decodedImageData = Buffer.from(imageData, 'base64') + + // workout the image hash + // i.e. 240e85ff2e4bb931f235985dd0134e459239496d2b5af6c5665168d38ef89b50 + const hash = crypto + .createHash('sha256') + .update(imageData) + .digest('hex') + + // workout the image folder + // i.e. 24/0e/85 + const rpath = _.join(path.sep, _.map(_.wrap(_.join, ''), _.take(3, _.chunk(2, _.split('', hash))))) + + // i.e. ..//idphotocard/24/0e/85 + const dirname = path.join(frontCameraBaseDir, rpath) + + // create the directory tree if needed + _.attempt(() => makeDir.sync(dirname)) + + // i.e. ..//idphotocard/24/0e/85/240e85ff2e4bb931f235985dd01....jpg + const filename = path.join(dirname, hash + '.jpg') + + // update db record patch + // i.e. { + // "idCardPhotoPath": "24/0e/85/240e85ff2e4bb931f235985dd01....jpg", + // "idCardPhotoAt": "now()" + // } + newPatch.frontCameraPath = path.join(rpath, hash + '.jpg') + newPatch.frontCameraAt = 'now()' + + // write image file + return writeFile(filename, decodedImageData) + .then(() => newPatch) + }) +} + +module.exports = { add, get, batch, getById, update, updatePhotoCard, updateFrontCamera } diff --git a/lib/routes.js b/lib/routes.js index ff7d508c..8ae3d440 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -233,6 +233,7 @@ function updateCustomer (req, res, next) { return compliance.validationPatch(req.deviceId, config, mergedCustomer) .then(_.merge(patch)) .then(newPatch => customers.updatePhotoCard(id, newPatch)) + .then(newPatch => customers.updateFrontCamera(id, newPatch)) .then(newPatch => customers.update(id, newPatch, null, txId)) }) .then(customer => respond(req, res, { customer })) diff --git a/package.json b/package.json index c66c8283..ccd742fc 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "lamassu-ofac-update": "./bin/lamassu-ofac-update", "lamassu-send-coins": "./bin/lamassu-send-coins", "lamassu-update-to-mnemonic": "./bin/lamassu-update-to-mnemonic", + "lamassu-configure-frontcamera": "./bin/lamassu-configure-frontcamera", "lamassu-ofac-update-sources": "./bin/lamassu-ofac-update-sources", "lamassu-devices": "./bin/lamassu-devices", "lamassu-operator": "./bin/lamassu-operator", diff --git a/public/elm.js b/public/elm.js index 5591b584..fc536c7a 100644 --- a/public/elm.js +++ b/public/elm.js @@ -35981,7 +35981,52 @@ var _user$project$Customer_View$customerView = function (customer) { }); } }(), - _1: {ctor: '[]'} + _1: { + ctor: '::', + _0: A2( + _elm_lang$html$Html$h2, + {ctor: '[]'}, + { + ctor: '::', + _0: _elm_lang$html$Html$text('Front Facing Camera Photo'), + _1: {ctor: '[]'} + }), + _1: { + ctor: '::', + _0: function () { + var _p3 = customer.frontCameraPath; + if (_p3.ctor === 'Nothing') { + return _elm_lang$html$Html$text('N/A'); + } else { + return A2( + _elm_lang$html$Html$div, + {ctor: '[]'}, + { + ctor: '::', + _0: A2( + _elm_lang$html$Html$img, + { + ctor: '::', + _0: _elm_lang$html$Html_Attributes$src( + A2(_elm_lang$core$Basics_ops['++'], '/front-camera-photo/', _p3._0)), + _1: { + ctor: '::', + _0: _elm_lang$html$Html_Attributes$height(200), + _1: { + ctor: '::', + _0: _elm_lang$html$Html_Attributes$alt('N/A'), + _1: {ctor: '[]'} + } + } + }, + {ctor: '[]'}), + _1: {ctor: '[]'} + }); + } + }(), + _1: {ctor: '[]'} + } + } } } } @@ -35990,8 +36035,8 @@ var _user$project$Customer_View$customerView = function (customer) { }); }; var _user$project$Customer_View$view = function (model) { - var _p3 = model; - switch (_p3.ctor) { + var _p4 = model; + switch (_p4.ctor) { case 'NotAsked': return A2( _elm_lang$html$Html$div, @@ -36013,7 +36058,7 @@ var _user$project$Customer_View$view = function (model) { { ctor: '::', _0: _elm_lang$html$Html$text( - _elm_lang$core$Basics$toString(_p3._0)), + _elm_lang$core$Basics$toString(_p4._0)), _1: {ctor: '[]'} }); default: @@ -36022,7 +36067,7 @@ var _user$project$Customer_View$view = function (model) { {ctor: '[]'}, { ctor: '::', - _0: _user$project$Customer_View$customerView(_p3._0), + _0: _user$project$Customer_View$customerView(_p4._0), _1: {ctor: '[]'} }); }