mirror of https://github.com/BTCPrivate/copay.git
commit
17614d2968
|
@ -49,6 +49,12 @@
|
|||
<i class="fi-loop icon-rotate"></i>
|
||||
<span translate> <strong class="size-16">Network Error</strong>.<br> Attempting to reconnect..</span>
|
||||
</span>
|
||||
<span class="status" ng-if="$root.needsEmailConfirmation">
|
||||
<i class="fi-alert"></i>
|
||||
<span translate> <strong class="size-16">email not confirmed</strong>.<br> Please confirm your email address
|
||||
using the confirmation link at the message we sent you</span>
|
||||
</span>
|
||||
|
||||
<nav class="tab-bar" ng-if="$root.iden" >
|
||||
<section class="left-small">
|
||||
<a class="left-off-canvas-toggle menu-icon" ><span></span></a>
|
||||
|
|
|
@ -41,7 +41,7 @@ angular.module('copayApp.controllers').controller('CreateController',
|
|||
|
||||
$scope.create = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
notification.error('Error', 'Please enter the required fields');
|
||||
$scope.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
var opts = {
|
||||
|
@ -56,7 +56,11 @@ angular.module('copayApp.controllers').controller('CreateController',
|
|||
$rootScope.starting = false;
|
||||
if (err || !wallet) {
|
||||
copay.logger.debug(err);
|
||||
$scope.error = 'Could not create wallet.' + err;
|
||||
if (err.match('OVERQUOTA')){
|
||||
$scope.error = 'Could not create wallet: storage limits on remove server exceeded';
|
||||
} else {
|
||||
$scope.error = 'Could not create wallet: ' + err;
|
||||
}
|
||||
}
|
||||
$rootScope.$digest()
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ angular.module('copayApp.controllers').controller('HomeController', function($sc
|
|||
|
||||
$scope.done = function() {
|
||||
$rootScope.starting = false;
|
||||
$rootScope.$digest();
|
||||
};
|
||||
|
||||
|
||||
|
@ -46,11 +47,13 @@ angular.module('copayApp.controllers').controller('HomeController', function($sc
|
|||
} else {
|
||||
$scope.error = 'Unknown error';
|
||||
}
|
||||
return $scope.done();
|
||||
}
|
||||
|
||||
if (iden) {
|
||||
iden.on('newWallet', $scope.done);
|
||||
iden.on('noWallets', $scope.done);
|
||||
iden.openWallets();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.controllers').controller('ProfileController', function($scope, $rootScope, $location, $modal, backupService, identityService) {
|
||||
angular.module('copayApp.controllers').controller('ProfileController', function($scope, $rootScope, $location, $modal, $filter, backupService, identityService) {
|
||||
$scope.username = $rootScope.iden.getName();
|
||||
$scope.isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
|
||||
|
||||
|
@ -20,16 +20,35 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
|
|||
identityService.deleteWallet(w, function(err) {
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
log.warn(err);
|
||||
copay.logger.warn(err);
|
||||
}
|
||||
$scope.setWallets();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
if ($rootScope.quotaPerItem) {
|
||||
$scope.perItem = $filter('noFractionNumber')($rootScope.quotaPerItem/1000,1);
|
||||
$scope.nrWallets =parseInt($rootScope.quotaItems) - 1;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setWallets = function() {
|
||||
if (!$rootScope.iden) return;
|
||||
$scope.wallets=$rootScope.iden.listWallets();
|
||||
};
|
||||
|
||||
var wallets = $rootScope.iden.listWallets();
|
||||
var max =$rootScope.quotaPerItem;
|
||||
|
||||
_.each(wallets, function(w) {
|
||||
var bits = w.sizes().total;
|
||||
w.kb = $filter('noFractionNumber')(bits/1000, 1);
|
||||
if (max) {
|
||||
w.usage = $filter('noFractionNumber')(bits/max * 100, 0);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.wallets = wallets;
|
||||
};
|
||||
|
||||
$scope.downloadWalletBackup = function(w) {
|
||||
if (!w) return;
|
||||
|
|
|
@ -122,7 +122,7 @@ Identity.open = function(opts, cb) {
|
|||
|
||||
var storage = opts.storage || opts.pluginManager.get('DB');
|
||||
storage.setCredentials(opts.email, opts.password, opts);
|
||||
storage.getItem(Identity.getKeyForEmail(opts.email), function(err, data) {
|
||||
storage.getItem(Identity.getKeyForEmail(opts.email), function(err, data, headers) {
|
||||
var exported;
|
||||
if (err) {
|
||||
return cb(err);
|
||||
|
@ -132,7 +132,7 @@ Identity.open = function(opts, cb) {
|
|||
} catch (e) {
|
||||
return cb(e);
|
||||
}
|
||||
return cb(null, new Identity(_.extend(opts, exported)));
|
||||
return cb(null, new Identity(_.extend(opts, exported)), headers);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -619,11 +619,10 @@ Identity.prototype.deleteWallet = function(walletId, cb) {
|
|||
delete this.focusedTimestamps[walletId];
|
||||
|
||||
this.storage.removeItem(Wallet.getStorageKey(walletId), function(err) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
if (err) return cb(err);
|
||||
self.emitAndKeepAlive('deletedWallet', walletId);
|
||||
self.store(null, cb);
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ EncryptedInsightStorage.prototype._brokenDecrypt = function(body) {
|
|||
EncryptedInsightStorage.prototype.getItem = function(name, callback) {
|
||||
var self = this;
|
||||
InsightStorage.prototype.getItem.apply(this, [name,
|
||||
function(err, body) {
|
||||
function(err, body, headers) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ EncryptedInsightStorage.prototype.getItem = function(name, callback) {
|
|||
log.debug('Could not decrypt value.');
|
||||
return callback('PNOTFOUND');
|
||||
}
|
||||
return callback(null, decryptedJson);
|
||||
return callback(null, decryptedJson, headers);
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -46,12 +46,12 @@ InsightStorage.prototype.getItem = function(name, callback) {
|
|||
var passphrase = this.getPassphrase();
|
||||
var self = this;
|
||||
|
||||
this._makeGetRequest(passphrase, name, function(err, body) {
|
||||
if (err) log.info('[InsightStorage. err]', err);
|
||||
this._makeGetRequest(passphrase, name, function(err, body, headers) {
|
||||
if (err) log.warn(err);
|
||||
if (err && err.indexOf('PNOTFOUND') !== -1 && mayBeOldPassword(self.password)) {
|
||||
return self._brokenGetItem(name, callback);
|
||||
}
|
||||
return callback(err, body);
|
||||
return callback(err, body, headers);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -74,6 +74,30 @@ InsightStorage.prototype.getPassphrase = function() {
|
|||
return bitcore.util.twoSha256(this.getKey()).toString('base64');
|
||||
};
|
||||
|
||||
/**
|
||||
* XmlHttpRequest's getAllResponseHeaders() method returns a string of response
|
||||
* headers according to the format described here:
|
||||
* http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders-method
|
||||
* This method parses that string into a user-friendly key/value pair object.
|
||||
*/
|
||||
InsightStorage.parseResponseHeaders = function (headerStr) {
|
||||
var headers = {};
|
||||
if (!headerStr) {
|
||||
return headers;
|
||||
}
|
||||
var headerPairs = headerStr.split('\u000d\u000a');
|
||||
for (var i = 0, len = headerPairs.length; i < len; i++) {
|
||||
var headerPair = headerPairs[i];
|
||||
var index = headerPair.indexOf('\u003a\u0020');
|
||||
if (index > 0) {
|
||||
var key = headerPair.substring(0, index);
|
||||
var val = headerPair.substring(index + 2);
|
||||
headers[key] = val;
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
InsightStorage.prototype._makeGetRequest = function(passphrase, key, callback) {
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
var retrieveUrl = this.storeUrl + '/retrieve';
|
||||
|
@ -96,7 +120,7 @@ InsightStorage.prototype._makeGetRequest = function(passphrase, key, callback) {
|
|||
if (response.statusCode !== 200) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
return callback(null, body);
|
||||
return callback(null, body, InsightStorage.parseResponseHeaders(response.getAllResponseHeaders()));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -172,16 +196,39 @@ InsightStorage.prototype.setItem = function(name, value, callback) {
|
|||
}
|
||||
if (response.statusCode === 409) {
|
||||
return callback('BADCREDENTIALS: Invalid username or password');
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
} else if (response.statusCode === 406) {
|
||||
return callback('OVERQUOTA: Quota exceeded');
|
||||
} else if (response.statusCode !== 200) {
|
||||
return callback('Unable to store data on insight');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
InsightStorage.prototype.removeItem = function(name, callback) {
|
||||
this.setItem(name, '', callback);
|
||||
InsightStorage.prototype.removeItem = function(key, callback) {
|
||||
var passphrase = this.getPassphrase();
|
||||
var authHeader = new buffers.Buffer(this.email + ':' + passphrase).toString('base64');
|
||||
var deleteUrl = this.storeUrl + '/delete/item';
|
||||
var getParams = {
|
||||
url: deleteUrl + '?' + querystring.encode({
|
||||
key: key
|
||||
}),
|
||||
headers: {
|
||||
'Authorization': authHeader
|
||||
}
|
||||
};
|
||||
log.debug('erase ' + name);
|
||||
this.request.get(getParams, function(err, response, body) {
|
||||
if (err) {
|
||||
return callback('Connection error');
|
||||
}
|
||||
if (response.statusCode === 409) {
|
||||
return callback('BADCREDENTIALS: Invalid username or password');
|
||||
} else if (response.statusCode !== 200) {
|
||||
return callback('Unable to remove data on insight');
|
||||
}
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
InsightStorage.prototype.clear = function(callback) {
|
||||
|
|
|
@ -67,6 +67,21 @@ angular.module('copayApp.services')
|
|||
|
||||
};
|
||||
|
||||
root.setServerStatus = function(headers) {
|
||||
if (!headers)
|
||||
return;
|
||||
|
||||
if (headers['X-Email-Needs-Validation'])
|
||||
$rootScope.needsEmailConfirmation = true;
|
||||
else
|
||||
$rootScope.needsEmailConfirmation = null;
|
||||
|
||||
if (headers['X-Quota-Per-Item'])
|
||||
$rootScope.quotaPerItem = parseInt(headers['X-Quota-Per-Item']);
|
||||
|
||||
if (headers['X-Quota-Items-Limit'])
|
||||
$rootScope.quotaItems = parseInt(headers['X-Quota-Items-Limit']);
|
||||
};
|
||||
|
||||
root.open = function(email, password, cb) {
|
||||
var opts = {
|
||||
|
@ -79,11 +94,11 @@ angular.module('copayApp.services')
|
|||
passphraseConfig: config.passphraseConfig,
|
||||
};
|
||||
|
||||
copay.Identity.open(opts, function(err, iden) {
|
||||
copay.Identity.open(opts, function(err, iden, headers) {
|
||||
if (err) return cb(err);
|
||||
root.setServerStatus(headers);
|
||||
root.bind(iden);
|
||||
iden.openWallets();
|
||||
return cb();
|
||||
return cb(null, iden);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -251,7 +266,6 @@ angular.module('copayApp.services')
|
|||
copay.logger.debug('newWallet:', w.getName(), wid, iden.getLastFocusedWalletId());
|
||||
root.installWalletHandlers(w);
|
||||
if (wid == iden.getLastFocusedWalletId()) {
|
||||
$rootScope.starting = false;
|
||||
copay.logger.debug('GOT Focused wallet:', w.getName());
|
||||
root.setFocusedWallet(w, true);
|
||||
root.goWalletHome();
|
||||
|
|
|
@ -12,6 +12,7 @@ describe('insight storage plugin', function() {
|
|||
var password = '1234';
|
||||
|
||||
var data = '{"random": true}';
|
||||
var headers = 'X-test: 12\r\nX-testb: 32';
|
||||
var namespace = 'profile::0000000000000000000000000000000000000000';
|
||||
|
||||
var oldSecret = 'rFA+F/N+ZvKXp717zBdfCKYQ5v9Fjry0W6tautj5etIH' + 'KLQliZBEYXA7AXjTJ9K3DglzGWJKost3QJUCMbhM/A=='
|
||||
|
@ -47,7 +48,8 @@ describe('insight storage plugin', function() {
|
|||
|
||||
var setupForRetrieval = function() {
|
||||
requestMock.get.onFirstCall().callsArgWith(1, null, {
|
||||
statusCode: 200
|
||||
statusCode: 200,
|
||||
getAllResponseHeaders: sinon.stub().returns(headers),
|
||||
}, data);
|
||||
};
|
||||
|
||||
|
@ -62,6 +64,20 @@ describe('insight storage plugin', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should be able to retrieve headers', function(done) {
|
||||
|
||||
setupForRetrieval();
|
||||
|
||||
storage.getItem(namespace, function(err, retrieved, headers) {
|
||||
assert(!err);
|
||||
headers['X-test'].should.equal('12');
|
||||
headers['X-testb'].should.equal('32');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
var setupForSave = function() {
|
||||
requestMock.post.onFirstCall().callsArgWith(1, null, {
|
||||
statusCode: 200
|
||||
|
@ -106,11 +122,13 @@ describe('insight storage plugin', function() {
|
|||
statusCode: 403
|
||||
});
|
||||
requestMock.get.onSecondCall().callsArgWith(1, null, {
|
||||
statusCode: 200
|
||||
statusCode: 200,
|
||||
getAllResponseHeaders: sinon.stub(),
|
||||
}, data);
|
||||
requestMock.post = sinon.stub();
|
||||
requestMock.post.onFirstCall().callsArgWith(1, null, {
|
||||
statusCode: 200
|
||||
statusCode: 200,
|
||||
getAllResponseHeaders: sinon.stub(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ describe("Unit: Controllers", function() {
|
|||
'e': 'errmsg',
|
||||
'loading': false
|
||||
});
|
||||
w.sizes = sinon.stub().returns({tota:1234});
|
||||
w.getBalance = sinon.stub().returns(10000);
|
||||
w.publicKeyRing = sinon.stub().yields(null);
|
||||
w.publicKeyRing.nicknameForCopayer = sinon.stub().returns('nickcopayer');
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
<form name="setupForm" ng-submit="create(setupForm)" novalidate>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<div class="box-notification" ng-show="error">
|
||||
<div class="box-icon error">
|
||||
<i class="fi-x size-24"></i>
|
||||
</div>
|
||||
<span class="text-warning size-14">
|
||||
{{error|translate}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="panel">
|
||||
<label><span translate>Wallet name</span>
|
||||
<div class="input">
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
<a class="text-white" href="#!/createProfile">creating your profile</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="box-setup">
|
||||
<h1><span translate>Sign in to</span> <b>Copay</b></h1>
|
||||
<form name="loginForm" ng-submit="openProfile(loginForm)" novalidate>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="backup" ng-controller="ProfileController" ng-init="setWallets()">
|
||||
<div class="backup" ng-controller="ProfileController" ng-init="init()">
|
||||
<div class="row hide-for-large-up">
|
||||
<div class="large-12 medium-12 small-12 columns">
|
||||
<h1>{{$root.title}}</h1>
|
||||
|
@ -40,7 +40,7 @@
|
|||
|
||||
<div class="line-dashed-h m20b"></div>
|
||||
|
||||
<div class="row" ng-init="getWallets()">
|
||||
<div class="row" ng-init="setWallets()">
|
||||
<div class="large-12 columns">
|
||||
<h2>Manage wallets</h2>
|
||||
<table>
|
||||
|
@ -73,7 +73,8 @@
|
|||
</td>
|
||||
<td>
|
||||
<span>
|
||||
{{item.sizes().total/1000}} kB
|
||||
{{item.kb}} kB
|
||||
<span ng-if="item.usage">({{item.usage}}%) </span>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
|
@ -93,6 +94,15 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="size-12">
|
||||
<div ng-if="perItem">
|
||||
<p> Your current Insight server usage quotas are: {{perItem}}kB per walelt and up to {{nrWallets}} wallets.
|
||||
</div>
|
||||
<div ng-if="$root.needsEmailConfirmation">
|
||||
<p> <i class="fi-alert"></i> Confirming for email with increase your storage usage limits.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
Loading…
Reference in New Issue