From 86295c9cf0f6672dc10a90714e243b2fe0ae57b9 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Mon, 16 Jun 2014 15:44:22 -0300 Subject: [PATCH 1/4] add backup service --- js/services/backup.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 js/services/backup.js diff --git a/js/services/backup.js b/js/services/backup.js new file mode 100644 index 000000000..64d20d55c --- /dev/null +++ b/js/services/backup.js @@ -0,0 +1,6 @@ +'use strict'; + +var BackupService = function() { +}; + +angular.module('copayApp.services').value('backup', new BackupService()); From b40038a554b5f6f1f08519e031f7bfd405306c84 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Mon, 16 Jun 2014 16:46:17 -0300 Subject: [PATCH 2/4] extract backup into service --- index.html | 1 + js/app.js | 4 +- js/controllers/backup.js | 66 ++++++++------------------------ js/controllers/signin.js | 74 ++++++++++++++++++++++++------------ js/services/backup.js | 6 --- js/services/backupService.js | 47 +++++++++++++++++++++++ 6 files changed, 115 insertions(+), 83 deletions(-) delete mode 100644 js/services/backup.js create mode 100644 js/services/backupService.js diff --git a/index.html b/index.html index caf3c0eb1..de4a5840c 100644 --- a/index.html +++ b/index.html @@ -849,6 +849,7 @@ on supported browsers please check http://www.w + diff --git a/js/app.js b/js/app.js index 2060979fc..5dea3f699 100644 --- a/js/app.js +++ b/js/app.js @@ -26,13 +26,13 @@ var copayApp = window.copayApp = angular.module('copayApp',[ 'monospaced.qrcode', 'notifications', 'copayApp.filters', + 'copayApp.services', 'copayApp.controllers', 'copayApp.directives', - 'copayApp.services', ]); angular.module('copayApp.filters', []); +angular.module('copayApp.services', []); angular.module('copayApp.controllers', []); angular.module('copayApp.directives', []); -angular.module('copayApp.services', []); diff --git a/js/controllers/backup.js b/js/controllers/backup.js index 92cc40f9b..3e4eb6c0a 100644 --- a/js/controllers/backup.js +++ b/js/controllers/backup.js @@ -1,69 +1,33 @@ 'use strict'; angular.module('copayApp.controllers').controller('BackupController', - function($scope, $rootScope, $location, $window, $timeout, $modal) { + function($scope, $rootScope, $location, $window, $timeout, $modal, backupService) { $scope.title = 'Backup'; - var _getEncryptedWallet = function() { - var wallet = $rootScope.wallet.toEncryptedObj(); - return wallet; - }; - $scope.download = function() { - var timestamp = +(new Date); - var walletName = ($rootScope.wallet.name ? $rootScope.wallet.name : '') + '-' + $rootScope.wallet.id ; - var filename = walletName + '-' + timestamp + '.json.aes'; - var wallet = _getEncryptedWallet(); - var blob = new Blob([wallet], {type: 'text/plain;charset=utf-8'}); - // show a native save dialog if we are in the shell - // and pass the wallet to the shell to convert to node Buffer - if (window.cshell) { - return window.cshell.send('backup:download', { - name: walletName, - wallet: wallet - }); - } - // otherwise lean on the browser implementation - saveAs(blob, filename); + backupService.download($rootScope.wallet); }; - $scope.openModal = function () { - var modalInstance = $modal.open({ - templateUrl: 'backupModal.html', - controller: ModalInstanceCtrl, - }); + $scope.openModal = function() { + var modalInstance = $modal.open({ + templateUrl: 'backupModal.html', + controller: ModalInstanceCtrl, + }); - modalInstance.result.then(sendEmail); - }; + modalInstance.result.then(function(email) { + backupService.sendEmail(email, $rootScope.wallet); + }); + }; - var sendEmail = function(email) { - var body = _getEncryptedWallet(); - var subject = ($rootScope.wallet.name ? $rootScope.wallet.name + ' - ' : '') + $rootScope.wallet.id; - var href = 'mailto:' + email + '?' - + 'subject=[Copay Backup] ' + subject + '&' - + 'body=' + body; + }); - if (window.cshell) { - return window.cshell.send('backup:email', href); - } +var ModalInstanceCtrl = function($scope, $modalInstance) { - var newWin = $window.open(href, '_blank', 'scrollbars=yes,resizable=yes,width=10,height=10'); - - if (newWin) { - $timeout(function() { - newWin.close(); - }, 1000); - } - }; -}); - -var ModalInstanceCtrl = function ($scope, $modalInstance) { - - $scope.submit = function (form) { + $scope.submit = function(form) { $modalInstance.close($scope.email); }; - $scope.cancel = function () { + $scope.cancel = function() { $modalInstance.dismiss('cancel'); }; }; diff --git a/js/controllers/signin.js b/js/controllers/signin.js index 2c522efba..d6c5be450 100644 --- a/js/controllers/signin.js +++ b/js/controllers/signin.js @@ -2,9 +2,10 @@ angular.module('copayApp.controllers').controller('SigninController', function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase) { - var cmp = function(o1, o2){ - var v1 = o1.show.toLowerCase(), v2 = o2.show.toLowerCase(); - return v1 > v2 ? 1 : ( v1 < v2 ) ? -1 : 0; + var cmp = function(o1, o2) { + var v1 = o1.show.toLowerCase(), + v2 = o2.show.toLowerCase(); + return v1 > v2 ? 1 : (v1 < v2) ? -1 : 0; }; $rootScope.videoInfo = {}; $scope.loading = false; @@ -14,23 +15,31 @@ angular.module('copayApp.controllers').controller('SigninController', $scope.open = function(form) { if (form && form.$invalid) { - $rootScope.$flashMessage = { message: 'Please, enter required fields', type: 'error'}; + $rootScope.$flashMessage = { + message: 'Please, enter required fields', + type: 'error' + }; return; } - + $scope.loading = true; var password = form.openPassword.$modelValue; - Passphrase.getBase64Async(password, function(passphrase){ + Passphrase.getBase64Async(password, function(passphrase) { var w, errMsg; - try{ - var w = walletFactory.open($scope.selectedWalletId, { passphrase: passphrase}); - } catch (e){ + try { + var w = walletFactory.open($scope.selectedWalletId, { + passphrase: passphrase + }); + } catch (e) { errMsg = e.message; }; if (!w) { $scope.loading = false; - $rootScope.$flashMessage = { message: errMsg || 'Wrong password', type: 'error'}; + $rootScope.$flashMessage = { + message: errMsg || 'Wrong password', + type: 'error' + }; $rootScope.$digest(); return; } @@ -40,31 +49,48 @@ angular.module('copayApp.controllers').controller('SigninController', $scope.join = function(form) { if (form && form.$invalid) { - $rootScope.$flashMessage = { message: 'Please, enter required fields', type: 'error'}; + $rootScope.$flashMessage = { + message: 'Please, enter required fields', + type: 'error' + }; return; } $scope.loading = true; - walletFactory.network.on('badSecret', function() { - }); + walletFactory.network.on('badSecret', function() {}); - Passphrase.getBase64Async($scope.joinPassword, function(passphrase){ - walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, function(err,w) { + Passphrase.getBase64Async($scope.joinPassword, function(passphrase) { + walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, passphrase, function(err, w) { $scope.loading = false; if (err || !w) { - if (err === 'joinError') - $rootScope.$flashMessage = { message: 'Can not find peer'}; + if (err === 'joinError') + $rootScope.$flashMessage = { + message: 'Can\'t find peer' + }; else if (err === 'walletFull') - $rootScope.$flashMessage = { message: 'The wallet is full', type: 'error'}; + $rootScope.$flashMessage = { + message: 'The wallet is full', + type: 'error' + }; else if (err === 'badNetwork') - $rootScope.$flashMessage = { message: 'The wallet your are trying to join uses a different Bitcoin Network. Check your settings.', type: 'error'}; - else if (err === 'badSecret') - $rootScope.$flashMessage = { message: 'Bad secret secret string', type: 'error'}; - else - $rootScope.$flashMessage = { message: 'Unknown error', type: 'error'}; + $rootScope.$flashMessage = { + message: 'The wallet your are trying to join uses a different Bitcoin Network. Check your settings.', + type: 'error' + }; + else if (err === 'badSecret') + $rootScope.$flashMessage = { + message: 'Bad secret secret string', + type: 'error' + }; + else + $rootScope.$flashMessage = { + message: 'Unknown error', + type: 'error' + }; controllerUtils.onErrorDigest(); } else { - controllerUtils.startNetwork(w, $scope); + backupService.download(w); + controllerUtils.startNetwork(w, $scope); } }); }); diff --git a/js/services/backup.js b/js/services/backup.js deleted file mode 100644 index 64d20d55c..000000000 --- a/js/services/backup.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; - -var BackupService = function() { -}; - -angular.module('copayApp.services').value('backup', new BackupService()); diff --git a/js/services/backupService.js b/js/services/backupService.js new file mode 100644 index 000000000..8e5d64455 --- /dev/null +++ b/js/services/backupService.js @@ -0,0 +1,47 @@ +'use strict'; + +var BackupService = function() {}; + +BackupService.prototype.getName = function(wallet) { + return (wallet.name ? (wallet.name+'-') : '') + wallet.id; +}; + +BackupService.prototype.download = function(wallet) { + var ew = wallet.toEncryptedObj(); + var timestamp = +(new Date()); + var walletName = this.getName(wallet); + var filename = walletName + '-' + timestamp + '.json.aes'; + var blob = new Blob([ew], { + type: 'text/plain;charset=utf-8' + }); + // show a native save dialog if we are in the shell + // and pass the wallet to the shell to convert to node Buffer + if (window.cshell) { + return window.cshell.send('backup:download', { + name: walletName, + wallet: ew + }); + } + // otherwise lean on the browser implementation + saveAs(blob, filename); +}; + +BackupService.prototype.sendEmail = function(email, wallet) { + var ew = wallet.toEncryptedObj(); + var body = ew; + var subject = this.getName(wallet); + var href = 'mailto:' + email + '?' + 'subject=[Copay Backup] ' + subject + '&' + 'body=' + body; + + if (window.cshell) { + return window.cshell.send('backup:email', href); + } + + var newWin = window.open(href, '_blank', 'scrollbars=yes,resizable=yes,width=10,height=10'); + if (newWin) { + setTimeout(function() { + newWin.close(); + }, 1000); + } +}; + +angular.module('copayApp.services').value('backupService', new BackupService()); From eeb621b6016bf7e37a4af650c9aa0a0792e23364 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Mon, 16 Jun 2014 16:54:50 -0300 Subject: [PATCH 3/4] added auto backup after create/join --- js/controllers/setup.js | 3 ++- js/controllers/signin.js | 2 +- js/services/backupService.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/js/controllers/setup.js b/js/controllers/setup.js index bdc9d8a49..ce36e12ef 100644 --- a/js/controllers/setup.js +++ b/js/controllers/setup.js @@ -33,7 +33,7 @@ var valid_pairs = { }; angular.module('copayApp.controllers').controller('SetupController', - function($scope, $rootScope, $location, $timeout, walletFactory, controllerUtils, Passphrase) { + function($scope, $rootScope, $location, $timeout, walletFactory, controllerUtils, Passphrase, backupService) { $rootScope.videoInfo = {}; $scope.loading = false; @@ -84,6 +84,7 @@ angular.module('copayApp.controllers').controller('SetupController', passphrase: passphrase, }; var w = walletFactory.create(opts); + backupService.download(w); controllerUtils.startNetwork(w, $scope); }); }; diff --git a/js/controllers/signin.js b/js/controllers/signin.js index d6c5be450..f639871ba 100644 --- a/js/controllers/signin.js +++ b/js/controllers/signin.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('SigninController', - function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase) { + function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase, backupService) { var cmp = function(o1, o2) { var v1 = o1.show.toLowerCase(), v2 = o2.show.toLowerCase(); diff --git a/js/services/backupService.js b/js/services/backupService.js index 8e5d64455..982d3376a 100644 --- a/js/services/backupService.js +++ b/js/services/backupService.js @@ -10,7 +10,7 @@ BackupService.prototype.download = function(wallet) { var ew = wallet.toEncryptedObj(); var timestamp = +(new Date()); var walletName = this.getName(wallet); - var filename = walletName + '-' + timestamp + '.json.aes'; + var filename = walletName + '-' + timestamp + '-keybackup.json.aes'; var blob = new Blob([ew], { type: 'text/plain;charset=utf-8' }); From 4b81bc1fdbf08b115f49bc8e0d33c002a1b30ca7 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Mon, 16 Jun 2014 17:40:59 -0300 Subject: [PATCH 4/4] add tests --- test/mocks/FakeWallet.js | 6 ++++++ test/unit/services/servicesSpec.js | 21 +++++++++++++++++++++ util/build.js | 5 +++++ 3 files changed, 32 insertions(+) diff --git a/test/mocks/FakeWallet.js b/test/mocks/FakeWallet.js index ec7f4fade..cfdcc75b9 100644 --- a/test/mocks/FakeWallet.js +++ b/test/mocks/FakeWallet.js @@ -3,6 +3,7 @@ var FakeWallet = function(){ this.balance=10000; this.safeBalance=1000; this.balanceByAddr={'1CjPR7Z5ZSyWk6WtXvSFgkptmpoi4UM9BC': 1000}; + this.name = 'myTESTwullet'; }; FakeWallet.prototype.set = function(balance, safeBalance, balanceByAddr){ @@ -28,6 +29,11 @@ FakeWallet.prototype.getBalance=function(cb){ return cb(null, this.balance, this.balanceByAddr, this.safeBalance); }; + +FakeWallet.prototype.toEncryptedObj = function() { + return 'SUPERENCRYPTEDSICRITSTUFF'; +}; + // This mock is meant for karma, module.exports is not necesary. try { module.exports = require('soop')(FakeWallet); diff --git a/test/unit/services/servicesSpec.js b/test/unit/services/servicesSpec.js index f272d51dd..65fed5a81 100644 --- a/test/unit/services/servicesSpec.js +++ b/test/unit/services/servicesSpec.js @@ -87,3 +87,24 @@ describe("Unit: controllerUtils", function() { }); + + +describe("Unit: Backup Service", function() { + var sinon = require('../sinon'); + beforeEach(angular.mock.module('copayApp.services')); + it('should contain a backup service', inject(function(backupService) { + expect(backupService).not.to.equal(null); + })); + it('should backup in file', inject(function(backupService) { + var mock = sinon.mock(window); + var expectation = mock.expects('saveAs'); + backupService.download(new FakeWallet()); + expectation.once(); + })); + it('should backup by email', inject(function(backupService) { + var mock = sinon.mock(window); + var expectation = mock.expects('open'); + backupService.sendEmail('fake@test.com', new FakeWallet()); + expectation.once(); + })); +}); diff --git a/util/build.js b/util/build.js index 7a1be5ffe..220162f47 100755 --- a/util/build.js +++ b/util/build.js @@ -80,6 +80,11 @@ var createBundle = function(opts) { expose: '../js/models/core/Passphrase' }); + if (opts.dontminify) { + b.require('sinon', { + expose: '../sinon' + }); + } if (!opts.dontminify) { b.transform({ global: true