🔨 Display backed up snaps in Web UI (Close #3)

This commit is contained in:
Sebastien Clement 2020-01-06 17:19:44 +01:00
parent c403ec5079
commit cc9f35e1ec
4 changed files with 168 additions and 47 deletions

View File

@ -43,16 +43,29 @@ router.get('/formated-local-snap', function(req, res, next) {
}); });
router.get('/formated-remote-manual', function(req, res, next) { router.get('/formated-backup-manual', function(req, res, next) {
webdav.init(true, 'cloud.seb6596.ovh', 'admin', 'WPHRG-4jwCw-i8eqg-mtiao-Kmwrw').then(() => { webdav.getFolderContent('/Hassio Backup/Manual/')
console.log('success'); .then((contents) => {
}, (err) => { contents.sort((a, b) => {
console.log('failure'); if (moment(a.lastmod).isBefore(moment(b.lastmod)))
console.log(err); return 1;
}) else
return -1;
})
res.render('backupSnaps',{backups: contents, moment: moment});
});
}); });
router.get('/formated-backup-auto', function(req, res, next) {
webdav.getFolderContent('/Hassio Backup/Auto/')
.then((contents) => {
res.render('backupSnaps',{backups: contents, moment: moment});
});
});
router.post('/nextcloud-settings', function(req, res, next) { router.post('/nextcloud-settings', function(req, res, next) {
let settings = req.body; let settings = req.body;
@ -90,7 +103,7 @@ router.post('/manual-backup', function(req, res, next) {
let id = req.query.id; let id = req.query.id;
let name = req.query.name; let name = req.query.name;
let status = statusTools.getStatus(); let status = statusTools.getStatus();
if (status.status == "creating" && status.status == "upload" && status.status == "download"){ if (status.status == "creating" && status.status == "upload" && status.status == "download") {
res.status(503); res.status(503);
res.send(); res.send();
return; return;
@ -112,7 +125,7 @@ router.post('/manual-backup', function(req, res, next) {
router.post('/new-backup', function(req, res, next) { router.post('/new-backup', function(req, res, next) {
let status = statusTools.getStatus(); let status = statusTools.getStatus();
if (status.status == "creating" && status.status == "upload" && status.status == "download"){ if (status.status == "creating" && status.status == "upload" && status.status == "download") {
res.status(503); res.status(503);
res.send(); res.send();
return; return;
@ -147,9 +160,6 @@ router.post('/backup-settings', function(req, res, next) {
res.status(400); res.status(400);
res.send(); res.send();
} }
}); });

View File

@ -163,10 +163,10 @@ class WebdavTools {
let lastPercent = 0; let lastPercent = 0;
let req = request.put(option) let req = request.put(option)
.on('drain', () => { .on('drain', () => {
let percent = Math.floor((req.req.connection.bytesWritten / fileSize)*100); let percent = Math.floor((req.req.connection.bytesWritten / fileSize) * 100);
if(lastPercent != percent){ if (lastPercent != percent) {
lastPercent = percent; lastPercent = percent;
status.progress = percent/100; status.progress = percent / 100;
statusTools.setStatus(status); statusTools.setStatus(status);
} }
@ -180,7 +180,7 @@ class WebdavTools {
reject(status.message); reject(status.message);
}).on('response', (res) => { }).on('response', (res) => {
if (res.statusCode != 204) { if (res.statusCode != 201) {
status.status = "error"; status.status = "error";
status.error_code = 4; status.error_code = 4;
status.message = "Fail to upload snapshot to nextcloud (Status code: " + res.statusCode + ") !" status.message = "Fail to upload snapshot to nextcloud (Status code: " + res.statusCode + ") !"
@ -205,6 +205,17 @@ class WebdavTools {
}); });
} }
getFolderContent(path) {
return new Promise((resolve, reject) => {
this.client.getDirectoryContents(path)
.then((contents)=>{
resolve(contents);
}).catch((error)=>{
reject(error);
})
});
}

View File

@ -0,0 +1,48 @@
<% if (locals.backups) { %>
<div class="collection">
<% for(const index in backups) { %>
<a class="collection-item local-snap-listener modal-trigger" href="#modal-<%=backups[index].etag%>"
data-id="<%= backups[index].etag %>">
<div><%= backups[index].basename%><div class="secondary-content">
<%= moment(backups[index].lastmod).format('MMM D, YYYY HH:mm') %></div>
</div>
</a>
<div id="modal-<%=backups[index].etag%>" class="modal modal-fixed-footer blue-grey darken-4 white-text">
<div class="modal-content">
<div class="row">
<div class="col s12 center">
<h4>Backup Detail</h2>
</div>
</div>
<div class="row">
<div class="col s12 center divider">
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input disabled type="text" id="name-<%=backups[index].etag%>" value="<%= backups[index].basename %>" />
<label for="name-<%=backups[index].etag%>" class="white-text active">Name</label>
</div>
<div class="input-field col s12">
<input disabled type="text" id="date-<%=backups[index].etag%>"
value="<%=moment(backups[index].lastmod).format('MMM D, YYYY HH:mm')%>" />
<label for="date-<%=backups[index].etag%>" class="white-text active">Date</label>
</div>
</div>
</div>
<div class="modal-footer blue-grey darken-4">
<a href="#!" class="modal-close waves-effect waves-green btn red">Close</a>
</div>
</div>
<% } %>
</div>
<% } %>

View File

@ -131,12 +131,14 @@
</div> </div>
<div class="col s12 m3"> <div class="col s12 m3">
<div class="card cyan darken-3"> <div class="card cyan darken-3">
<div class="card-content" > <div class="card-content">
<span class="card-title white-text" style="font-weight: bold;">Manual </span> <span class="card-title white-text" style="font-weight: bold;">Manual </span>
<div class="divider"></div> <div class="divider"></div>
<div style="width: 100%;" class="center"> <div style="width: 100%;" class="center">
<a class="btn green center waves-effect waves-light" id="btn-backup-now" style="margin-top: 7px;">Backup Now</a> <a class="btn green center waves-effect waves-light" id="btn-backup-now" style="margin-top: 7px;">Backup
<a class="btn center teal darken-4 waves-effect waves-light" id="btn-clean-now" style="margin-top: 7px;">Clean Now</a> Now</a>
<a class="btn center teal darken-4 waves-effect waves-light" id="btn-clean-now"
style="margin-top: 7px;">Clean Now</a>
</div> </div>
</div> </div>
@ -154,6 +156,19 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col s12 m12 l6 ">
<div class="card cyan darken-3">
<div class="card-content">
<span class="card-title center white-text">Nextcloud Backups</span>
<span class="card-title center white-text">Auto</span>
<span class="card-title center white-text divider"></span>
<div id="auto_backups"></div>
<span class="card-title center white-text">Manu</span>
<span class="card-title center white-text divider"></span>
<div id="manual_backups"></div>
</div>
</div>
</div>
</div> </div>
</div> </div>
@ -353,6 +368,8 @@
<script> <script>
var last_status = ""; var last_status = "";
var last_local_snap = ""; var last_local_snap = "";
var last_manu_back = "";
var last_auto_back = "";
var loadingModal = null; var loadingModal = null;
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
@ -369,34 +386,69 @@
}); });
function updateDynamicListeners() {
var elems = document.querySelectorAll('.collapsible');
M.Collapsible.init(elems, { accordion: true });
var modals = document.querySelectorAll('.modal:not(#modal-loading)');
M.Modal.init(modals, { dismissible: false });
let loadingModals = document.querySelectorAll('#modal-loading');
M.Modal.init(loadingModals, { dismissible: false });
loadingModal = M.Modal.getInstance(document.querySelector('#modal-loading'));
$('.local-snap-listener').click(function() {
let id = this.getAttribute('data-id');
console.log(id);
});
$('.manual-back-list').click(function() {
let id = this.getAttribute('data-id');
let name = this.getAttribute('data-name');
manualBackup(id, name);
})
}
function updateLocalSnaps() { function updateLocalSnaps() {
let needUpdate = false;
$.get('./api/formated-local-snap', (data) => { $.get('./api/formated-local-snap', (data) => {
if (JSON.stringify(data) === last_local_snap) if (JSON.stringify(data) === last_local_snap)
return; return;
last_local_snap = JSON.stringify(data); last_local_snap = JSON.stringify(data);
needUpdate = true;
$('#local_snaps').empty(); $('#local_snaps').empty();
$('#local_snaps').html(data); $('#local_snaps').html(data);
}).always(() => {
updateManuBackup(needUpdate);
});
}
var elems = document.querySelectorAll('.collapsible'); function updateManuBackup(prevUpdate) {
M.Collapsible.init(elems, { accordion: true }); let needUpdate = false;
var modals = document.querySelectorAll('.modal:not(#modal-loading)'); $.get('./api/formated-backup-manual', (data) => {
M.Modal.init(modals, { dismissible: false }); if (JSON.stringify(data) === last_manu_back)
return;
last_manu_back = JSON.stringify(data);
needUpdate = true;
$('#manual_backups').empty();
$('#manual_backups').html(data);
let loadingModals = document.querySelectorAll('#modal-loading'); }).always(() => {
M.Modal.init(loadingModals, { dismissible: false }); updateAutoBackup(prevUpdate || needUpdate);
});
loadingModal = M.Modal.getInstance(document.querySelector('#modal-loading')); }
$('.local-snap-listener').click(function() { function updateAutoBackup(prevUpdate) {
let id = this.getAttribute('data-id'); let needUpdate = false;
console.log(id); $.get('./api/formated-backup-auto', (data) => {
}); if (JSON.stringify(data) === last_auto_back)
return;
$('.manual-back-list').click(function() { needUpdate = true;
let id = this.getAttribute('data-id'); last_auto_back = JSON.stringify(data);
let name = this.getAttribute('data-name'); $('#auto_backups').empty();
manualBackup(id, name); $('#auto_backups').html(data);
})
}).always(() => {
if (prevUpdate || needUpdate)
updateDynamicListeners();
}); });
} }
@ -407,23 +459,23 @@
switch (data.status) { switch (data.status) {
case "error": case "error":
printStatus('Error', data.message); printStatus('Error', data.message);
$('#btn-backup-now').removeClass("disabled"); $('#btn-backup-now, #btn-clean-now').removeClass("disabled");
break; break;
case "idle": case "idle":
printStatus('Idle', "Waiting for next backup."); printStatus('Idle', "Waiting for next backup.");
$('#btn-backup-now').removeClass("disabled"); $('#btn-backup-now, #btn-clean-now').removeClass("disabled");
break; break;
case "download": case "download":
printStatusWithBar('Downloading Snapshot', data.progress); printStatusWithBar('Downloading Snapshot', data.progress);
$('#btn-backup-now').addClass("disabled"); $('#btn-backup-now, #btn-clean-now').addClass("disabled");
break; break;
case "upload": case "upload":
printStatusWithBar('Uploading Snapshot', data.progress); printStatusWithBar('Uploading Snapshot', data.progress);
$('#btn-backup-now').addClass("disabled"); $('#btn-backup-now, #btn-clean-now').addClass("disabled");
break; break;
case "creating": case "creating":
printStatusWithBar('Creating Snapshot', data.progress); printStatusWithBar('Creating Snapshot', data.progress);
$('#btn-backup-now').addClass("disabled"); $('#btn-backup-now, #btn-clean-now').addClass("disabled");
break; break;
} }
if (data.last_backup != null) { if (data.last_backup != null) {
@ -629,9 +681,9 @@
.done(() => { .done(() => {
M.toast({ html: '<i class="material-icons" style="margin-right:10px">check_box</i> Backup settings saved !', classes: "green" }); M.toast({ html: '<i class="material-icons" style="margin-right:10px">check_box</i> Backup settings saved !', classes: "green" });
M.Modal.getInstance(document.querySelector('#modal-settings-backup')).close(); M.Modal.getInstance(document.querySelector('#modal-settings-backup')).close();
}).fail(()=>{ }).fail(() => {
M.toast({ html: '<i class="material-icons" style="margin-right:10px">warning</i> Can\'t save backup settings !', classes: "red" }); M.toast({ html: '<i class="material-icons" style="margin-right:10px">warning</i> Can\'t save backup settings !', classes: "red" });
}).always(()=>{ }).always(() => {
loadingModal.close(); loadingModal.close();
}); });
} }