1
0
Fork 0
mirror of https://github.com/Luzifer/vault-otp-ui.git synced 2024-11-09 08:40:05 +00:00

Add error handling to frontend

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2017-06-14 23:15:15 +02:00
parent 6d730a849c
commit 72dcb026e9
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
4 changed files with 91 additions and 10 deletions

View file

@ -1,12 +1,14 @@
currentTimeout = 0 currentTimeout = 0
clipboard = undefined clipboard = undefined
# document-ready function to start Javascript processing
$ -> $ ->
if $('body').hasClass 'state-signedin' if $('body').hasClass 'state-signedin'
initializeApplication() initializeApplication()
# createOTPItem generates code entries from the JSON objects passed from the backend
createOTPItem = (item) -> createOTPItem = (item) ->
tpl = $('#otp-item').html() tpl = $('#tpl-otp-item').html()
otpItem = $(tpl) otpItem = $(tpl)
otpItem.find('.badge').text item.code.replace(/^(.{3})(.{3})$/, '$1 $2') otpItem.find('.badge').text item.code.replace(/^(.{3})(.{3})$/, '$1 $2')
@ -15,15 +17,46 @@ createOTPItem = (item) ->
otpItem.appendTo $('#keylist') otpItem.appendTo $('#keylist')
# createAlert adds a colored message at the top of the list
# type = success / info / warning / danger
createAlert = (type, keyword, message, timeout) ->
tpl = $('#tpl-message').html()
alrt = $(tpl)
alrt.addClass "alert-#{type}"
alrt.find('.keyword').text keyword
alrt.find('.message').text message
alrt.appendTo $('#messagecontainer')
if timeout > 0
delay timeout, () ->
alrt.remove()
# delay is a convenience wrapper to swap parameters of setTimeout
delay = (delayMSecs, fkt) -> delay = (delayMSecs, fkt) ->
window.setTimeout fkt, delayMSecs window.setTimeout fkt, delayMSecs
# fetchCodes contacts the backend to receive JSON containing current codes
fetchCodes = () -> fetchCodes = () ->
$.ajax $.ajax
url: 'codes.json', url: 'codes.json',
success: updateCodes, success: updateCodes,
dataType: 'json', dataType: 'json',
error: () ->
createAlert 'danger', 'Oops.', 'Server could not be contacted. Maybe you (or the server) are offline? I will retry in a few seconds.', 5000
delay 5000, fetchCodes
statusCode:
401: () ->
window.location.reload()
500: () ->
createAlert 'danger', 'Oops.', 'The server responded with an internal error. I will retry in a few seconds.', 2000
delay 2000, fetchCodes
# filterChange is called when changing the filter field and matches the
# titles of all shown entries. Those not matching the given regular expression
# will be hidden. The filterChange function is also called after a successful
# refresh of the shown codes to re-apply
filterChange = () -> filterChange = () ->
filter = $('#filter').val().toLowerCase() filter = $('#filter').val().toLowerCase()
$('.otp-item').each (idx, el) -> $('.otp-item').each (idx, el) ->
@ -32,25 +65,33 @@ filterChange = () ->
else else
$(el).show() $(el).show()
# initializeApplication initializes some basic events and starts the first
# polling for codes
initializeApplication = () -> initializeApplication = () ->
$('#keylist').empty() $('#keylist').empty()
$('#filter').bind 'keyup', filterChange $('#filter').bind 'keyup', filterChange
tick 500, refreshTimerProgress tick 500, refreshTimerProgress
fetchCodes() fetchCodes()
# refreshTimerProgress updates the top progressbar to display the # refreshTimerProgress updates the top progressbar to display the
# remaining time until the one-time-passwords changes # remaining time until the one-time-passwords changes
refreshTimerProgress = () -> refreshTimerProgress = () ->
secondsLeft = timeLeft() secondsLeft = timeLeft()
$('#timer').css 'width', "#{secondsLeft / 30 * 100}%" $('#timer').css 'width', "#{secondsLeft / 30 * 100}%"
# tick is a convenience wrapper to swap parameters of setInterval
tick = (delay, fkt) -> tick = (delay, fkt) ->
window.setInterval fkt, delay window.setInterval fkt, delay
# timeLeft calculates the remaining time until codes get invalid
timeLeft = () -> timeLeft = () ->
now = new Date().getTime() now = new Date().getTime()
(currentTimeout - now) / 1000 (currentTimeout - now) / 1000
# updateCodes is being called when the backend delivered codes. The codes
# are then rendered and the clipboard methods are re-bound. Afterwards the
# next fetchCodes call is timed to that moment when the codes are getting
# invalid
updateCodes = (data) -> updateCodes = (data) ->
currentTimeout = new Date(data.next_wrap).getTime() currentTimeout = new Date(data.next_wrap).getTime()

View file

@ -1,6 +1,6 @@
// Generated by CoffeeScript 1.12.4 // Generated by CoffeeScript 1.12.4
(function() { (function() {
var clipboard, createOTPItem, currentTimeout, delay, fetchCodes, filterChange, initializeApplication, refreshTimerProgress, tick, timeLeft, updateCodes; var clipboard, createAlert, createOTPItem, currentTimeout, delay, fetchCodes, filterChange, initializeApplication, refreshTimerProgress, tick, timeLeft, updateCodes;
currentTimeout = 0; currentTimeout = 0;
@ -14,7 +14,7 @@
createOTPItem = function(item) { createOTPItem = function(item) {
var otpItem, tpl; var otpItem, tpl;
tpl = $('#otp-item').html(); tpl = $('#tpl-otp-item').html();
otpItem = $(tpl); otpItem = $(tpl);
otpItem.find('.badge').text(item.code.replace(/^(.{3})(.{3})$/, '$1 $2')); otpItem.find('.badge').text(item.code.replace(/^(.{3})(.{3})$/, '$1 $2'));
otpItem.find('.title').text(item.name); otpItem.find('.title').text(item.name);
@ -22,6 +22,21 @@
return otpItem.appendTo($('#keylist')); return otpItem.appendTo($('#keylist'));
}; };
createAlert = function(type, keyword, message, timeout) {
var alrt, tpl;
tpl = $('#tpl-message').html();
alrt = $(tpl);
alrt.addClass("alert-" + type);
alrt.find('.keyword').text(keyword);
alrt.find('.message').text(message);
alrt.appendTo($('#messagecontainer'));
if (timeout > 0) {
return delay(timeout, function() {
return alrt.remove();
});
}
};
delay = function(delayMSecs, fkt) { delay = function(delayMSecs, fkt) {
return window.setTimeout(fkt, delayMSecs); return window.setTimeout(fkt, delayMSecs);
}; };
@ -30,7 +45,20 @@
return $.ajax({ return $.ajax({
url: 'codes.json', url: 'codes.json',
success: updateCodes, success: updateCodes,
dataType: 'json' dataType: 'json',
error: function() {
createAlert('danger', 'Oops.', 'Server could not be contacted. Maybe you (or the server) are offline? I will retry in a few seconds.', 5000);
return delay(5000, fetchCodes);
},
statusCode: {
401: function() {
return window.location.reload();
},
500: function() {
createAlert('danger', 'Oops.', 'The server responded with an internal error. I will retry in a few seconds.', 2000);
return delay(2000, fetchCodes);
}
}
}); });
}; };

File diff suppressed because one or more lines are too long

View file

@ -19,6 +19,7 @@
body { font-size: 16px; padding-top: 90px; } body { font-size: 16px; padding-top: 90px; }
i { margin-right: 0.4em; } i { margin-right: 0.4em; }
#templates { display: none; } #templates { display: none; }
.alert { background-image: none; }
.badge { background-color: #e2e2e2; color: #555; font-size: 15px; font-weight: bold; margin-top: 3px; } .badge { background-color: #e2e2e2; color: #555; font-size: 15px; font-weight: bold; margin-top: 3px; }
.center { text-align: center; } .center { text-align: center; }
.jumbotron h2 { text-align: center; } .jumbotron h2 { text-align: center; }
@ -71,6 +72,11 @@
<div id="application"> <div id="application">
<div class="container"> <div class="container">
<div class="row">
<div class="col-xs-12 col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2" id="messagecontainer">
</div>
</div>
<div class="row"> <div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3"> <div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3">
<div class="list-group" id="keylist"> <div class="list-group" id="keylist">
@ -107,13 +113,19 @@
</div> <!-- /#login --> </div> <!-- /#login -->
<div id="templates"> <div id="templates">
<div id="otp-item"> <div id="tpl-otp-item">
<a href="#" class="list-group-item otp-item"> <a href="#" class="list-group-item otp-item">
<span class="badge">145 369</span> <span class="badge">145 369</span>
<i class="fa"></i> <i class="fa"></i>
<span class="title">Some Site</span> <span class="title">Some Site</span>
</a> </a>
</div> </div>
<div id="tpl-message">
<div class="alert alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<strong class="keyword">Warning!</strong> <span class="message"></span>
</div>
</div>
</div> </div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->