Skip to content

Commit

Permalink
Fixes #17: Records loading is very slow if several MB of elements are…
Browse files Browse the repository at this point in the history
… present.
  • Loading branch information
evilsocket committed Dec 8, 2017
1 parent a55d288 commit f7ba4bc
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 60 deletions.
14 changes: 14 additions & 0 deletions arc/css/style.css
Expand Up @@ -372,3 +372,17 @@ ul li.placeholder:before {
width: 100%;
font-size: 0.8rem;
}

.loader {
border: 16px solid #f3f3f3;
border-top: 16px solid #3498db;
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
3 changes: 2 additions & 1 deletion arc/index.html
Expand Up @@ -41,6 +41,7 @@
<body>
<div class="container-fluid" ng-app="PM" ng-controller="PMController">
<div ng-include src="'/views/modals/error.html?v=2'"></div>
<div ng-include src="'/views/modals/loader.html'"></div>

<!-- if user is not logged, show login -->
<div id="login" class="container" ng-if="!arc.IsLogged()" ng-include src="'/views/login.html?v=3'"></div>
Expand Down Expand Up @@ -119,7 +120,7 @@
</li>

<li class="list-group-item" id="secret_list_item_{{ r.ID }}" ng-if="filterSecret(r)" ng-repeat="r in arc.records" ng-click="onShowSecret(r)">
<span ng-include src="'/views/secret_list_item.html?v=14'"></span>
<span ng-include src="'/views/secret_list_item.html?v=15'"></span>
</li>
</ul>

Expand Down
22 changes: 15 additions & 7 deletions arc/js/api.js
Expand Up @@ -15,7 +15,7 @@ function Arc(on_req_executed) {
this.req_started = null;
this.req_time = null;
this.on_req_executed = on_req_executed || function(success, req, time){
console.log( req + " executed in " + time + " ms." );
console.log( req + " executed " + ( success ? "succesfully" : "with errors" ) + " in " + time + " ms." );
};
}

Expand All @@ -38,8 +38,9 @@ Arc.prototype.onRequestDone = function( success ) {
this.on_req_executed( success, this.req, this.req_time );
}

Arc.prototype.Api = function( method, path, data, success, error ) {
console.log( method + ' ' + path );
Arc.prototype.Api = function( method, path, data, success, error, raw ) {
var dataType = raw ? undefined : 'json';
console.log( method + ' ' + path + ' (' + dataType + ')' );

this.onRequestStart( method + ' ' + path );

Expand All @@ -62,8 +63,8 @@ Arc.prototype.Api = function( method, path, data, success, error ) {
},
data: data ? JSON.stringify(data) : null,
contentType: "application/json",
dataType: 'json',
timeout: 60 * 60 * 100
dataType: dataType,
timeout: 60 * 60 * 1000
});
}

Expand Down Expand Up @@ -135,7 +136,12 @@ Arc.prototype.SetStore = function( id, success, error ) {
error);
}

Arc.prototype.AddRecord = function( title, expire_at, prune, data, encryption, success, error ) {
Arc.prototype.GetRecordBuffer = function( record_id, success, error ) {
var path = '/api/store/' + this.store.ID + '/record/' + record_id + '/buffer';
this.Api( 'GET', path, null, success, error, true );
};

Arc.prototype.AddRecord = function( title, expire_at, prune, data, encryption, size, success, error ) {
if( this.HasStore() == false ) {
return error("No store has been selected.");
}
Expand All @@ -146,12 +152,13 @@ Arc.prototype.AddRecord = function( title, expire_at, prune, data, encryption, s
'ExpiredAt': expire_at,
'Prune': prune,
'Encryption': encryption,
'Size': size,
};

this.Api( 'POST', '/api/store/' + this.store.ID + '/records', record, success, error );
}

Arc.prototype.UpdateRecord = function( id, title, expire_at, prune, data, encryption, success, error) {
Arc.prototype.UpdateRecord = function( id, title, expire_at, prune, data, encryption, size, success, error) {
if( this.HasStore() == false ) {
return error("No store has been selected.");
}
Expand All @@ -163,6 +170,7 @@ Arc.prototype.UpdateRecord = function( id, title, expire_at, prune, data, encryp
'Prune': prune,
'Data': data,
'Encryption': encryption,
'Size': size
};
this.Api( 'PUT', '/api/store/' + this.store.ID + '/record/' + id, record, success, error );
}
Expand Down
130 changes: 95 additions & 35 deletions arc/js/app.js
Expand Up @@ -130,7 +130,9 @@ app.controller('PMController', ['$scope', function (scope) {
scope.errorMessage = message;

if( message ) {
if( typeof(message) == 'object' ) {
console.log(message);

if( typeof(message) == 'object' && message.responseJSON ) {
message = message.responseJSON.message;
}

Expand All @@ -143,6 +145,7 @@ app.controller('PMController', ['$scope', function (scope) {
};

scope.errorHandler = function(error) {
scope.hideLoader();
scope.setError(error);
scope.$apply();
};
Expand All @@ -162,6 +165,34 @@ app.controller('PMController', ['$scope', function (scope) {
return true;
};

scope.isLoading = function() {
// bootstrap < 4: isShown
// bootstra >= 4: _isShown
return !!( $('#loader_modal').data('bs.modal') || {} )._isShown;
};

scope.showLoader = function(message, callback) {
$('#loader_message').text(message);
if( !scope.isLoading() ) {
// console.log("LOADER SHOWING");
$('#loader_modal').on('shown.bs.modal', callback ).modal({
backdrop: 'static',
keyboard: false
});
} else if( callback ) {
callback();
}
};

scope.hideLoader = function() {
$('#loader_message').text('');
if( scope.isLoading() ) {
// console.log("LOADER HIDING");
// https://stackoverflow.com/questions/14451052/in-twitter-bootstrap-how-do-i-unbind-an-event-from-the-closing-of-a-modal-dialo
$('#loader_modal').unbind().modal('hide');
}
};

scope.getStore = function(success, force) {
if( force == false && scope.arc.HasStore() == false ) {
alert("No store selected");
Expand All @@ -170,9 +201,12 @@ app.controller('PMController', ['$scope', function (scope) {
scope.setStatus("Loading passwords store ...");
scope.arc.SetStore( scope.store_id, function() {
document.title = scope.arc.store.Title;

scope.hideLoader();
scope.setupTimeout();
scope.setError(null);
scope.$apply();
success();
},
scope.errorHandler );
}
Expand Down Expand Up @@ -229,14 +263,24 @@ app.controller('PMController', ['$scope', function (scope) {
};
};

scope.Timeout = {
max: 1000 * 60 * 60 * 12,
min: 1000
};

scope.setTimeoutIfLess = function(record) {
var expires = Date.parse(record.ExpiredAt),
now = Date.now();

if( expires > now || ( expires <= now && record.Prune ) ) {
var tm = expires - now;
if( scope.timeout == null || this.timeout.ms > tm ) {
this.setTimeoutTo(tm);
if( tm <= scope.Timeout.max ) {
if( tm < scope.Timeout.min ) {
tm = scope.Timeout.min;
}
if( scope.timeout == null || this.timeout.ms > tm ) {
this.setTimeoutTo(tm);
}
}
}
};
Expand Down Expand Up @@ -337,7 +381,8 @@ app.controller('PMController', ['$scope', function (scope) {

for( var i = 0; i < fields.length; ++i ) {
var entry = $.extend( true, {}, fields[i] );
entry.RenderToList( list, nidx );
var eidx = list.find('li').length;
entry.RenderToList( list, eidx );
}

$('#field_selector_modal').modal('hide');
Expand Down Expand Up @@ -459,40 +504,55 @@ app.controller('PMController', ['$scope', function (scope) {
record.AddEntry(new Entry( type, name, value ));
}

data = record.Encrypt( scope.key )

scope.arc.AddRecord( title, expire_at, prune, data, 'aes', function(record) {
scope.getStore( function() {
scope.$apply();
});
},
scope.errorHandler );

$('#secret_modal').modal('hide');

scope.showLoader("Encrypting record ...", function(){
// Execute asynchronously to not block the ui.
setTimeout( function() {
var data = record.Encrypt( scope.key );
var size = data.length;
scope.arc.AddRecord( title, expire_at, prune, data, 'aes', size, function(record) {
scope.getStore(function() {});
},
scope.errorHandler );
}, 0 );
});
};

scope.onShowSecret = function(secret) {
var record = new Record(secret.Title);

record.Decrypt( scope.key, secret.Data );

if( record.HasError() == true ) {
$('#record_error_' + secret.ID).html(record.error);
$('#record_status_' + secret.ID ).addClass("status-error");
}
else {
scope.setSecret(secret)

$('#record_lock_' + secret.ID ).removeClass("fa-lock").addClass("fa-unlock");
$('#record_status_' + secret.ID ).removeClass("status-locked").addClass("status-unlocked");

scope.showSecretModal(false, record.title, secret.UpdatedAt, secret.ExpiredAt, secret.Prune);
console.log( "Loading record " + secret.ID );

scope.showLoader( "Buffering data ...", function() {
// start reading data when loader is shown
scope.arc.GetRecordBuffer( secret.ID, function(data){
console.log( "Decrypting record data ..." );
// start decrypting data when message is updated
scope.showLoader( "Decrypting data ...", function() {
var record = new Record(secret.Title);
record.Decrypt( scope.key, data );
if( record.HasError() == true ) {
$('#record_error_' + secret.ID).html(record.error);
$('#record_status_' + secret.ID ).addClass("status-error");
}
else {
scope.setSecret(secret)

$('#record_lock_' + secret.ID ).removeClass("fa-lock").addClass("fa-unlock");
$('#record_status_' + secret.ID ).removeClass("status-locked").addClass("status-unlocked");

scope.showSecretModal(false, record.title, secret.UpdatedAt, secret.ExpiredAt, secret.Prune);

var list = $('#secret_entry_list');
for( var i = 0; i < record.entries.length; i++ ){
record.entries[i].RenderToList( list, i );
}
}

scope.hideLoader();
});

var list = $('#secret_entry_list');
for( var i = 0; i < record.entries.length; i++ ){
record.entries[i].RenderToList( list, i );
}
}
}, scope.errorHandler );
});
};

scope.onUpdate = function() {
Expand Down Expand Up @@ -539,8 +599,8 @@ app.controller('PMController', ['$scope', function (scope) {
}

var data = record.Encrypt( scope.key )

scope.arc.UpdateRecord( scope.secret.ID, title, expire_at, prune, data, 'aes', function(record) {
var size = data.length
scope.arc.UpdateRecord( scope.secret.ID, title, expire_at, prune, data, 'aes', size, function(record) {
scope.setSecret(null);
scope.setError(null);
scope.getStore( function() {
Expand Down
7 changes: 6 additions & 1 deletion arc/js/libs/jquery/jquery.editable.js
Expand Up @@ -62,7 +62,12 @@
var f_size = $(this).css('font-size');
var input_id = '#editable_input_for_' + $(this).attr('id');
$(this).hide();
$(input_id).css('font-size', f_size).val( $(this).text() ).show().focus();
$(input_id).css({
'font-size': f_size,
})
.val( $(this).text() )
.show()
.focus();
});

var onHide = function(e){
Expand Down

0 comments on commit f7ba4bc

Please sign in to comment.