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:
parent
6d730a849c
commit
72dcb026e9
4 changed files with 91 additions and 10 deletions
|
@ -1,12 +1,14 @@
|
|||
currentTimeout = 0
|
||||
clipboard = undefined
|
||||
|
||||
# document-ready function to start Javascript processing
|
||||
$ ->
|
||||
if $('body').hasClass 'state-signedin'
|
||||
initializeApplication()
|
||||
|
||||
# createOTPItem generates code entries from the JSON objects passed from the backend
|
||||
createOTPItem = (item) ->
|
||||
tpl = $('#otp-item').html()
|
||||
tpl = $('#tpl-otp-item').html()
|
||||
|
||||
otpItem = $(tpl)
|
||||
otpItem.find('.badge').text item.code.replace(/^(.{3})(.{3})$/, '$1 $2')
|
||||
|
@ -15,15 +17,46 @@ createOTPItem = (item) ->
|
|||
|
||||
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) ->
|
||||
window.setTimeout fkt, delayMSecs
|
||||
|
||||
# fetchCodes contacts the backend to receive JSON containing current codes
|
||||
fetchCodes = () ->
|
||||
$.ajax
|
||||
url: 'codes.json',
|
||||
success: updateCodes,
|
||||
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 = () ->
|
||||
filter = $('#filter').val().toLowerCase()
|
||||
$('.otp-item').each (idx, el) ->
|
||||
|
@ -32,6 +65,8 @@ filterChange = () ->
|
|||
else
|
||||
$(el).show()
|
||||
|
||||
# initializeApplication initializes some basic events and starts the first
|
||||
# polling for codes
|
||||
initializeApplication = () ->
|
||||
$('#keylist').empty()
|
||||
$('#filter').bind 'keyup', filterChange
|
||||
|
@ -44,13 +79,19 @@ refreshTimerProgress = () ->
|
|||
secondsLeft = timeLeft()
|
||||
$('#timer').css 'width', "#{secondsLeft / 30 * 100}%"
|
||||
|
||||
# tick is a convenience wrapper to swap parameters of setInterval
|
||||
tick = (delay, fkt) ->
|
||||
window.setInterval fkt, delay
|
||||
|
||||
# timeLeft calculates the remaining time until codes get invalid
|
||||
timeLeft = () ->
|
||||
now = new Date().getTime()
|
||||
(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) ->
|
||||
currentTimeout = new Date(data.next_wrap).getTime()
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.12.4
|
||||
(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;
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
|||
|
||||
createOTPItem = function(item) {
|
||||
var otpItem, tpl;
|
||||
tpl = $('#otp-item').html();
|
||||
tpl = $('#tpl-otp-item').html();
|
||||
otpItem = $(tpl);
|
||||
otpItem.find('.badge').text(item.code.replace(/^(.{3})(.{3})$/, '$1 $2'));
|
||||
otpItem.find('.title').text(item.name);
|
||||
|
@ -22,6 +22,21 @@
|
|||
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) {
|
||||
return window.setTimeout(fkt, delayMSecs);
|
||||
};
|
||||
|
@ -30,7 +45,20 @@
|
|||
return $.ajax({
|
||||
url: 'codes.json',
|
||||
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
14
index.html
14
index.html
|
@ -19,6 +19,7 @@
|
|||
body { font-size: 16px; padding-top: 90px; }
|
||||
i { margin-right: 0.4em; }
|
||||
#templates { display: none; }
|
||||
.alert { background-image: none; }
|
||||
.badge { background-color: #e2e2e2; color: #555; font-size: 15px; font-weight: bold; margin-top: 3px; }
|
||||
.center { text-align: center; }
|
||||
.jumbotron h2 { text-align: center; }
|
||||
|
@ -71,6 +72,11 @@
|
|||
<div id="application">
|
||||
|
||||
<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="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">
|
||||
|
@ -107,13 +113,19 @@
|
|||
</div> <!-- /#login -->
|
||||
|
||||
<div id="templates">
|
||||
<div id="otp-item">
|
||||
<div id="tpl-otp-item">
|
||||
<a href="#" class="list-group-item otp-item">
|
||||
<span class="badge">145 369</span>
|
||||
<i class="fa"></i>
|
||||
<span class="title">Some Site</span>
|
||||
</a>
|
||||
</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">×</span></button>
|
||||
<strong class="keyword">Warning!</strong> <span class="message"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
|
|
Loading…
Reference in a new issue