From bf96453fb695bd032998fe94db6d2bb7c1d38b90 Mon Sep 17 00:00:00 2001 From: xurror Date: Wed, 13 May 2020 21:24:30 +0100 Subject: [PATCH] create user object and user endpoint --- .../bitcore-wallet-service/package-lock.json | 113 +++++++++++++++++- packages/bitcore-wallet-service/package.json | 1 + .../src/lib/expressapp.ts | 11 ++ .../src/lib/model/index.ts | 1 + .../src/lib/model/user.ts | 61 ++++++++++ .../bitcore-wallet-service/src/lib/server.ts | 105 +++++++++++++++- .../bitcore-wallet-service/src/lib/storage.ts | 38 +++++- 7 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 packages/bitcore-wallet-service/src/lib/model/user.ts diff --git a/packages/bitcore-wallet-service/package-lock.json b/packages/bitcore-wallet-service/package-lock.json index 340b461207a..db96efe48d7 100644 --- a/packages/bitcore-wallet-service/package-lock.json +++ b/packages/bitcore-wallet-service/package-lock.json @@ -320,6 +320,11 @@ "color-convert": "^1.9.0" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -560,6 +565,38 @@ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -698,6 +735,17 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -843,6 +891,38 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -1927,6 +2007,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, "mustache": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz", @@ -2017,6 +2112,11 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, "object-component": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", @@ -2592,6 +2692,11 @@ "resolved": "https://registry.npmjs.org/sticky-session/-/sticky-session-0.1.0.tgz", "integrity": "sha1-Mwij4d2+WVXLeeHe39Mu3EPJ74E=" }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", @@ -2851,6 +2956,11 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, "typescript": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", @@ -3001,8 +3111,7 @@ "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "yeast": { "version": "0.1.2", diff --git a/packages/bitcore-wallet-service/package.json b/packages/bitcore-wallet-service/package.json index 823afcb1e3a..6219a744c66 100644 --- a/packages/bitcore-wallet-service/package.json +++ b/packages/bitcore-wallet-service/package.json @@ -39,6 +39,7 @@ "moment": "^2.10.3", "mongodb": "^2.0.27", "morgan": "^1.9.1", + "multer": "^1.4.2", "mustache": "^2.1.0", "nodemailer": "^5.1.1", "npmlog": "^0.1.1", diff --git a/packages/bitcore-wallet-service/src/lib/expressapp.ts b/packages/bitcore-wallet-service/src/lib/expressapp.ts index 6a7d15ec559..a3a27d06053 100644 --- a/packages/bitcore-wallet-service/src/lib/expressapp.ts +++ b/packages/bitcore-wallet-service/src/lib/expressapp.ts @@ -436,6 +436,17 @@ export class ExpressApp { }); }); + router.post('/v1/user/new/', (req, res) => { + getServerWithAuth(req, res, server => { + server.createUser(req, (err, user) => { + if (err) return returnError(err, res, req); + res.json(user); + }) + }) + }); + + //router.get('v1/user/:id') + router.get('/v2/txproposals/', (req, res) => { getServerWithAuth(req, res, server => { server.getPendingTxs({}, (err, pendings) => { diff --git a/packages/bitcore-wallet-service/src/lib/model/index.ts b/packages/bitcore-wallet-service/src/lib/model/index.ts index 796bfb2d39d..1bce91b0386 100644 --- a/packages/bitcore-wallet-service/src/lib/model/index.ts +++ b/packages/bitcore-wallet-service/src/lib/model/index.ts @@ -9,3 +9,4 @@ export { TxConfirmationSub } from './txconfirmationsub'; export { TxNote } from './txnote'; export { ITxProposal, TxProposal } from './txproposal'; export { IWallet, Wallet } from './wallet'; +export { IUser, User } from './user' ; diff --git a/packages/bitcore-wallet-service/src/lib/model/user.ts b/packages/bitcore-wallet-service/src/lib/model/user.ts new file mode 100644 index 00000000000..98af4512ee8 --- /dev/null +++ b/packages/bitcore-wallet-service/src/lib/model/user.ts @@ -0,0 +1,61 @@ +export interface IUser { + id: number | string; + version: string; + createdOn: number; + name: string; + dateOfBirth: Date; + address: string; + mobileNumber: number; + document: string; + verified: boolean; + publicSharingKey: string; +} + +export class User { + id: number | string; + version: string; + createdOn: number; + name: string; + dateOfBirth: Date; + address: string; + mobileNumber: number; + document: string; + verified: boolean; + publicSharingKey: string; + + static create(opts) { + opts = opts || {}; + + const x = new User(); + + x.version = '1.0.0'; + x.createdOn = Math.floor(Date.now() / 1000); + x.id = opts.id; + x.name = opts.name; + x.dateOfBirth = opts.dateOfBirth; + x.address = opts.address; + x.mobileNumber = opts.mobileNumber; + x.document = opts.document; + x.verified = opts.verified; + x.publicSharingKey = opts.publicSharingKey; + + return x; + } + + static fromObj(obj) { + const x = new User(); + + x.version = obj.version; + x.createdOn = obj.createdOn; + x.id = obj.id; + x.name = obj.name; + x.dateOfBirth = obj.dateOfBirth; + x.address = obj.address; + x.mobileNumber = obj.mobileNumber; + x.document = obj.document; + x.verified = obj.verified; + x.publicSharingKey = obj.publicSharingKey; + + return x; + } +} \ No newline at end of file diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index 45fddc2de19..647fe5b47ae 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -22,10 +22,12 @@ import { TxConfirmationSub, TxNote, TxProposal, - Wallet + Wallet, + User } from './model'; import { Storage } from './storage'; +const path = require('path'); const config = require('../config'); const Uuid = require('uuid'); const $ = require('preconditions').singleton(); @@ -33,6 +35,19 @@ const deprecatedServerMessage = require('../deprecated-serverMessages'); const serverMessages = require('../serverMessages'); const BCHAddressTranslator = require('./bchaddresstranslator'); +const multer = require('multer'); + +const fileStorage = multer.diskStorage({ + destination: function(req, file, cb) { + cb(null, 'uploads/'); + }, + + // By default, multer removes file extensions so let's add them back + filename: function(req, file, cb) { + cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname)); + } +}); + log.debug = log.verbose; log.disableColor(); log.level = 'error'; @@ -1208,6 +1223,94 @@ export class WalletService { }); } + + + /*** Start New */ + + + /** + * Creates a new transaction proposal. + * @param {Object} opts + * @returns {User} User Info + */ + createUser(opts, cb) { + let obj = opts.body; + let file = opts.file; + + let upload = multer({ fileStorage: fileStorage }).single('document'); + + const checkUserAlreadyExists = (id, cb) => { + if (!id) return cb(); + this.storage.fetchUser(id, cb); + } + + this._runLocked( + cb, + cb => { + this.getWallet({}, (err, wallet) => { + if (err) return cb(err); + if (!wallet.isComplete()) return cb(Errors.WALLET_NOT_COMPLETE); + if (wallet.scanStatus == 'error') return cb(Errors.WALLET_NEED_SCAN); + + checkUserAlreadyExists(obj.id, (err, user) => { + if (err) return cb(err); + if (user) return cb(null, user); + + async.series( + [ + next => { + upload(file, cb, function(err) { + if (err instanceof multer.MulterError) return next(err); + else if (err) return next(err); + next() + }); + }, + next => { + const userOpts = { + id: obj.id, + version: obj.version, + name: obj.name, + dateOfBirth: obj.dateOfBirth, + address: obj.address, + mobileNumber: obj.mobileNumber, + document: file.fieldname, + verified: obj.verified, + publicSharingKey: obj.publicSharingKey + }; + user = User.create(userOpts); + next(); + } + ], + err => { + if (err) return cb(err); + return cb(null, user) + } + ) + }) + }) + } + ) + } + + /** + * Retrieves a preferences for the current wallet/copayer pair. + * @param {Object} opts + * @returns {Object} preferences + */ + getUser(opts, cb) { + this.storage.fetchUser(opts.id, (err, user) => { + if (err) return cb(err); + return cb(null, user || {}); + }); + } + + +/*** End New */ + + + + + _canCreateAddress(ignoreMaxGap, cb) { if (ignoreMaxGap) return cb(null, true); diff --git a/packages/bitcore-wallet-service/src/lib/storage.ts b/packages/bitcore-wallet-service/src/lib/storage.ts index 54b99b3ba0c..27f5ca95270 100644 --- a/packages/bitcore-wallet-service/src/lib/storage.ts +++ b/packages/bitcore-wallet-service/src/lib/storage.ts @@ -12,7 +12,8 @@ import { TxConfirmationSub, TxNote, TxProposal, - Wallet + Wallet, + User } from './model'; const BCHAddressTranslator = require('./bchaddresstranslator'); // only for migration @@ -36,7 +37,8 @@ const collections = { SESSIONS: 'sessions', PUSH_NOTIFICATION_SUBS: 'push_notification_subs', TX_CONFIRMATION_SUBS: 'tx_confirmation_subs', - LOCKS: 'locks' + LOCKS: 'locks', + USER: 'user' }; export class Storage { @@ -136,6 +138,9 @@ export class Storage { db.collection(collections.SESSIONS).createIndex({ copayerId: 1 }); + db.collection(collections.USER).createIndex({ + walletId: 1 + }); } connect(opts, cb) { @@ -203,6 +208,33 @@ export class Storage { ); } + fetchUser(id, cb) { + this.db.collection(collections.USER).find({ id }) + .sort({ + createdOn: 1 + }) + .toArray((err, result) => { + if (err) return cb(err); + if (!result) return cb(); + return cb(null, result.map(User.fromObj)); + }); + } + + storeUser(user, cb) { + //upload.single(user.document) + this.db.collection(collections.USER).update( + { + id: user.id + }, + user, + { + w: 1, + upsert: true + }, + cb + ); + } + storeWalletAndUpdateCopayersLookup(wallet, cb) { const copayerLookups = _.map(wallet.copayers, copayer => { try { @@ -864,7 +896,7 @@ export class Storage { } ); } - + getTxHistoryCacheStatusV8(walletId, cb) { this.db.collection(collections.CACHE).findOne( {