ncp-web: add backups panel

This commit is contained in:
nachoparker 2019-04-29 20:06:58 -06:00
parent 01cd4215a5
commit f34354c336
15 changed files with 747 additions and 26 deletions

View File

@ -100,7 +100,8 @@ tar $compress_arg -cf "$destfile" \
exit 1
}
rm "$dbbackup"
chmod 600 "$destfile"
chmod 640 "$destfile"
chown :www-data "$destfile"
echo "backup $destfile generated"
EOF

View File

@ -1,9 +1,13 @@
[v1.11.4](https://github.com/nextcloud/nextcloudpi/commit/62a7f45) (2019-04-28) letsencrypt: switch to apt version
[v1.12.0](https://github.com/nextcloud/nextcloudpi/commit/703ff6f) (2019-04-29) ncp-web: add backups panel
[v1.11.3 ](https://github.com/nextcloud/nextcloudpi/commit/71d8f52) (2019-04-09) nc-restore: check btrfs command
[v1.11.5](https://github.com/nextcloud/nextcloudpi/commit/01cd421) (2019-04-29) letsencrypt: force renewal by default
[v1.11.2, master](https://github.com/nextcloud/nextcloudpi/commit/3754609) (2019-04-06) armbian: fix uu
[v1.11.4 ](https://github.com/nextcloud/nextcloudpi/commit/b3c7d13) (2019-04-28) letsencrypt: switch to apt version
[v1.11.3 ](https://github.com/nextcloud/nextcloudpi/commit/02efd61) (2019-04-09) nc-restore: check btrfs command
[v1.11.2 ](https://github.com/nextcloud/nextcloudpi/commit/3754609) (2019-04-06) armbian: fix uu
[v1.11.1 ](https://github.com/nextcloud/nextcloudpi/commit/a712935) (2019-04-05) nc-backup: fix space calculation

159
ncp-web/backups.php Normal file
View File

@ -0,0 +1,159 @@
<!--
NextCloudPi Web Backups Panel
Copyleft 2019 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
GPL licensed (see end of file) * Use at your own risk!
More at https://nextcloudpi.com
-->
<?php
$bkp_cfg = file_get_contents('/usr/local/etc/ncp-config.d/nc-backup.cfg') or exit('backup config not found');
$bkp_auto_cfg = file_get_contents('/usr/local/etc/ncp-config.d/nc-backup-auto.cfg') or exit('backup config not found');
$bkp_json = json_decode($bkp_cfg , true) or exit('invalid format');
$bkp_auto_json = json_decode($bkp_auto_cfg, true) or exit('invalid format');
$bkp_dir = $bkp_json['params'][0]['value'];
$bkp_auto_dir = $bkp_auto_json['params'][1]['value'];
$bkps = array();
$bkps_auto = array();
if (file_exists($bkp_dir))
{
$bkps = array_diff(scandir($bkp_dir), array('.', '..'));
$bkps = preg_filter('/^/', $bkp_dir. '/', $bkps);
}
if (file_exists($bkp_auto_dir))
{
$bkps_auto = array_diff(scandir($bkp_auto_dir), array('.', '..'));
$bkps_auto = preg_filter('/^/', $bkp_auto_dir . '/', $bkps_auto);
}
$bkps = array_unique(array_merge($bkps, $bkps_auto));
if (!empty($bkps))
{
echo <<<HTML
<div id="backups-table">
<table class="dashtable backuptable">
<th>Date</th><th>Size</th><th>Compressed</th><th>Data</th><th></th>
HTML;
foreach ($bkps as $bkp)
{
$extension = pathinfo($bkp, PATHINFO_EXTENSION);
if ($extension === "tar" || $extension === "gz")
{
$compressed = "";
if ($extension === "gz")
$compressed = '✓';
$date = date("Y M d @ H:i", filemtime($bkp));
$size = round(filesize($bkp)/1024/1024) . " MiB";
$has_data = '';
exec("sudo /home/www/ncp-backup-launcher.sh bkp " . escapeshellarg($bkp) . " \"$compressed\"", $output, $ret);
if ($ret == 0)
$has_data = '✓';
echo <<<HTML
<tr id="$bkp">
<td class="long-field" title="$bkp">$date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td class="val-field">$size</td>
<td class="ok-field align-center">$compressed</td>
<td class="ok-field align-center">$has_data</td>
<td>
<img class="hidden-btn default-btn download-bkp" title="download" src="../img/download.svg">
<img class="hidden-btn default-btn delete-bkp" title="delete" src="../img/delete.svg">
<img class="hidden-btn default-btn restore-bkp" title="restore" src="../img/defaults.svg">
</td>
</tr>
HTML;
echo '<input type="hidden" name="csrf-token" value="' . getCSRFToken() . '"/>';
}
}
echo <<<HTML
</table>
</div>
HTML;
} else {
echo "<div>No backups found.</div>";
}
?>
</br></br>
<h2 class="text-title">Restore from file</h2>
<form action="upload.php" method="POST" enctype="multipart/form-data">
<div class="restore-upload-btn-wrapper">
<input type="file" name="backup" id="restore-upload" accept=".tar,.tar.gz"/>
<input id="restore-upload-btn" type="submit" value="Restore"/>
</div>
</form>
</br></br>
<h2 class="text-title"><?php echo $l->__("Snapshots"); ?></h2>
<?php
include( '/var/www/nextcloud/config/config.php' );
$snap_dir = realpath($CONFIG['datadirectory'] . '/../ncp-snapshots');
$snaps = array();
if (file_exists($snap_dir))
{
$snaps = array_diff(scandir($snap_dir), array('.', '..'));
$snaps = preg_filter('/^/', $snap_dir . '/', $snaps);
}
if (!empty($snaps))
{
echo <<<HTML
<div id="snapshots-table">
<table class="dashtable backuptable">
HTML;
foreach ($snaps as $snap)
{
exec("sudo /home/www/ncp-backup-launcher.sh chksnp " . escapeshellarg($snap), $out, $ret);
if ($ret == 0)
{
$snap_name = basename($snap);
echo <<<HTML
<tr id="$snap">
<td class="text-align-left" title="$snap">$snap_name</td>
<td>
<img class="hidden-btn default-btn delete-snap" title="delete" src="../img/delete.svg">
<img class="hidden-btn default-btn restore-snap" title="restore" src="../img/defaults.svg">
</td>
</tr>
HTML;
}
}
echo <<<HTML
</table>
</div>
HTML;
} else {
echo "<div>No snapshots found.</div>";
}
?>
<!--
License
This script is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This script is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this script; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
-->

View File

@ -1076,7 +1076,7 @@ select {
display: none;
}
#loading-info-gif {
.loading-section-gif {
display: flex;
justify-content: center;
align-items: center;
@ -1150,6 +1150,9 @@ select {
.icon-search {
background-image: url('../img/search.svg');
}
.icon-backups {
background-image: url('../img/defaults-white.svg');
}
.icon-config {
background-image: url('../img/settings-white.svg');
}
@ -1223,6 +1226,18 @@ a#versionlink:hover {
max-width: 210px;
}
#confirmation-dialog {
position:fixed;
top:0;
bottom:0;
height:100%;
width:100%;
background-color:rgba(0, 0, 0, 0.5);
z-index:9000;
text-align:center;
cursor:pointer;
}
.dialog {
display:block;
background: white;
@ -1249,7 +1264,7 @@ a#versionlink:hover {
opacity:0.75
}
#close-wizard {
.close-dialog-x {
position: absolute;
top: 5px;
right: 5px;
@ -1319,6 +1334,23 @@ a#versionlink:hover {
border-bottom: 1px solid #ebebeb;
}
.backuptable td {
text-align: right;
}
.text-align-left {
text-align: left !important;
padding-left: 1em;
}
.backuptable th {
text-align: center;
}
#backups-content div {
text-align: center;
}
.align-center {
text-align: center !important;
}
#dashboard-suggestions {
margin-bottom: 1em;
}
@ -1353,7 +1385,23 @@ a#versionlink:hover {
-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)';
opacity: 0.5;
}
.pwd-btn:hover, .default-btn:hover {
-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
opacity: 1;
.hidden-btn {
cursor: pointer;
-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)';
opacity: 0;
}
.backuptable tr:hover img {
-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)';
opacity: 0.5;
}
.pwd-btn:hover, .default-btn:hover {
-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)' !important;
opacity: 1 !important;
}
.restore-upload-btn-wrapper {
display: flex;
flex-direction: row;
align-items: center;
}

75
ncp-web/download.php Normal file
View File

@ -0,0 +1,75 @@
<?php
///
// NextCloudPi Web Panel backend
//
// Copyleft 2019 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
// GPL licensed (see end of file) * Use at your own risk!
//
// More at https://nextcloudpi.com
///
include ('csrf.php');
session_start();
// CSRF check
$token = isset($_REQUEST['token']) ? $_REQUEST['token'] : '';
if ( empty($token) || !validateCSRFToken($token) )
exit('Unauthorized download');
if (!isset($_REQUEST["bkp"]))
die();
$file = $_REQUEST["bkp"];
if (!file_exists($file))
die('File not found');
if (!is_readable($file))
die('NCP does not have read permissions on this file');
$size = filesize($file);
$extension = pathinfo($file, PATHINFO_EXTENSION);
if ($extension === "tar" )
$mime_type = 'application/x-tar';
else if( $extension === "gz")
$mime_type = 'application/x-gzip';
else
die();
ob_start();
ob_clean();
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime_type);
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file) . "\"");
header('Content-Length: ' . $size);
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
$chunksize = 8 * (1024 * 1024);
if($size > $chunksize)
{
$handle = fopen($file, 'rb') or die("Error opening file");
while (!feof($handle))
{
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
}
fclose($handle);
}
else
readfile($file);
ob_flush();
flush();
exit();
?>

View File

@ -123,7 +123,6 @@ function print_config_forms( $l /* translations l10n object */ )
$cfg_dir = '/usr/local/etc/ncp-config.d/';
$d_iterator = new RecursiveDirectoryIterator($bin_dir);
$iterator = new RecursiveIteratorIterator($d_iterator);
$objects = new RegexIterator($iterator, '/^.+\.sh$/i', RecursiveRegexIterator::GET_MATCH);
$ret = "";
$sections = array_diff(scandir($bin_dir), array('.', '..', 'l10n'));
@ -167,7 +166,6 @@ function print_sidebar( $l /* translations l10n object */, $ticks /* wether to c
$cfg_dir = '/usr/local/etc/ncp-config.d/';
$d_iterator = new RecursiveDirectoryIterator($bin_dir);
$iterator = new RecursiveIteratorIterator($d_iterator);
$objects = new RegexIterator($iterator, '/^.+\.sh$/i', RecursiveRegexIterator::GET_MATCH);
$ret = "";
$sections = array_diff(scandir($bin_dir), array('.', '..', 'l10n'));

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
version="1.1"
viewbox="0 0 16 16"
id="svg4"
sodipodi:docname="defaults-white.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="640"
inkscape:window-height="480"
id="namedview6"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg4" />
<path
d="m7.9319 2.4252c-3.4324 0-5.6787 2.9953-5.5301 5.8394h-1.8778l3.3924 3.4063 3.5454-3.3664h-1.8657c-0.20594-1.4772 1.0106-2.706 2.3366-2.6868 1.386 0.020855 2.4331 1.0688 2.4331 2.3758 0.07821 1.3851-1.4164 2.9788-3.4463 2.1985 0 1.0688 0.00261 2.2115 0 3.2717 3.641 0.72124 6.6389-2.1811 6.6389-5.431 0-3.0961-2.5374-5.6074-5.6266-5.6074z"
id="path2"
style="fill:#ffffff" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

1
ncp-web/img/delete.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="M6.5 1L6 2H3c-.554 0-1 .446-1 1v1h12V3c0-.554-.446-1-1-1h-3l-.5-1zM3 5l.875 9c.06.55.573 1 1.125 1h6c.552 0 1.064-.45 1.125-1L13 5z"/></svg>

After

Width:  |  Height:  |  Size: 247 B

1
ncp-web/img/download.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="M6 1h4v7h5l-7 7-7-7h5z"/></svg>

After

Width:  |  Height:  |  Size: 138 B

View File

@ -104,7 +104,7 @@ if ($ret == 0) {
<br>
<a href="wizard"> <button type="button" class="wizard-btn" id="go-wizard">{$l->__("run")} </button></a>
<button type="button" class="first-run-close" id="skip-wizard" >{$l->__("skip")} </button>
<button type="button" class="first-run-close" id="close-wizard">{$l->__("close")}</button>
<button type="button" class="first-run-close close-dialog-x">{$l->__("close")}</button>
<br><br>
</div>
</div>
@ -113,6 +113,20 @@ HTML;
}
?>
<div id="confirmation-dialog" class="hidden">
<div class='dialog'>
<br><br>
<h2 id="config-box-title">Are you sure?</h2>
<br>
<p>Click OK to confirm this operation</p>
<br>
<button type="button" id="confirmation-dialog-ok"> OK </button>
<button type="button" class="confirmation-dialog-close"> Cancel </button>
<button type="button" class="confirmation-dialog-close close-dialog-x">Close</button>
<br><br>
</div>
</div>
<header role="banner"><div id="header">
<div id="header-left">
<a href="https://ownyourbits.com" id="nextcloudpi" target="_blank" tabindex="1">
@ -166,6 +180,11 @@ HTML;
<div class="icon-dashboard"></div>
</div>
</div>
<div id="backups-btn" title="<?php echo $l->__("Backups and snapshots"); ?>">
<div class="expand">
<div class="icon-backups"></div>
</div>
</div>
<div id="config-btn" title="<?php echo $l->__("Nextcloud Configuration"); ?>">
<div class="expand">
<div class="icon-config"></div>
@ -213,7 +232,13 @@ HTML;
<h2 class="text-title"><?php echo $l->__("System Info"); ?></h2>
<div id="dashboard-suggestions" class="table-wrapper"></div>
<div id="dashboard-table" class="outputbox table-wrapper"></div>
<div id="loading-info-gif"> <img src="img/loading-small.gif"> </div>
<div id="loading-info-gif" class="loading-section-gif"> <img src="img/loading-small.gif"> </div>
</div>
<div id="backups-wrapper" class="content-box <?php if($_GET['app'] != 'backups') echo 'hidden';?>">
<h2 class="text-title"><?php echo $l->__("Backups"); ?></h2>
<div id="backups-content" class="table-wrapper"></div>
<div id="loading-backups-gif" class="loading-section-gif"> <img src="img/loading-small.gif"> </div>
</div>
<div id="nc-config-wrapper" class="content-box <?php if($_GET['app'] != 'config') echo 'hidden';?>">

View File

@ -4,20 +4,23 @@
// Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
// GPL licensed (see end of file) * Use at your own risk!
//
// More at https://ownyourbits.com/2017/02/13/nextcloud-ready-raspberry-pi-image/
// More at https://nextcloudpi.com
///
var MINI = require('minified');
var $ = MINI.$, $$ = MINI.$$, EE = MINI.EE;
var selectedID = null;
var selectedID = null;
var ncp_app_list = null;
var search_box = null;
var lock = false;
var search_box = null;
var lock = false;
// URL based navigation
// TODO unify repeating code
window.onpopstate = function(event) {
selectedID = location.search.split('=')[1];
if (selectedID == 'config')
if (selectedID == 'backups')
switch_to_section('backups');
else if (selectedID == 'config')
switch_to_section('nc-config');
else if (selectedID == 'dashboard')
switch_to_section('dashboard');
@ -27,14 +30,16 @@ window.onpopstate = function(event) {
function errorMsg()
{
$('#config-box').fill( "Something went wrong. Try refreshing the page" );
$('#app-content').fill( "Something went wrong. Try refreshing the page" );
}
function switch_to_section(section)
{
// TODO unify repeating code
$( '#config-wrapper > div' ).hide();
$( '#dashboard-wrapper' ).hide();
$( '#nc-config-wrapper' ).hide();
$( '#backups-wrapper' ).hide();
$( '#' + section + '-wrapper' ).show();
$( '#app-navigation ul' ).set('-active');
selectedID = null;
@ -123,10 +128,169 @@ function print_dashboard()
$('#loading-info-gif').hide();
$('#dashboard-table').ht( ret.table );
$('#dashboard-suggestions').ht( ret.suggestions );
reload_sidebar();
print_backups();
} ).error( errorMsg );
}
function del_bkp(button)
{
var tr = button.up().up();
var path = tr.get('.id');
$.request('post', 'ncp-launcher.php', { action:'del-bkp',
value: path,
csrf_token: $( '#csrf-token' ).get( '.value' ) }).then(
function success( result )
{
var ret = $.parseJSON( result );
if ( ret.token )
$('#csrf-token').set( { value: ret.token } );
if ( ret.ret && ret.ret == '0' ) // means that the process was launched
tr.remove();
else
console.log('failed removing ' + path);
}
).error( errorMsg )
}
function restore_bkp(button)
{
var tr = button.up().up();
var path = tr.get('.id');
click_app($('#nc-restore'));
history.pushState(null, selectedID, "?app=" + selectedID);
$('#nc-restore-BACKUPFILE').set({ value: path });
$('#nc-restore-config-button').trigger('click');
}
function restore_snap(button)
{
var tr = button.up().up();
var path = tr.get('.id');
click_app($('#nc-restore-snapshot'));
history.pushState(null, selectedID, "?app=" + selectedID);
$('#nc-restore-snapshot-SNAPSHOT').set({ value: path });
$('#nc-restore-snapshot-config-button').trigger('click');
}
function del_snap(button)
{
var tr = button.up().up();
var path = tr.get('.id');
$.request('post', 'ncp-launcher.php', { action:'del-snap',
value: path,
csrf_token: $('#csrf-token').get('.value') }).then(
function success( result )
{
var ret = $.parseJSON( result );
if ( ret.token )
$('#csrf-token').set( { value: ret.token } );
if ( ret.ret && ret.ret == '0' ) // means that the process was launched
tr.remove();
else
console.log('failed removing ' + path);
}
).error( errorMsg )
}
function restore_upload(button)
{
var file = $$('#restore-upload').files[0];
if (!file) return;
var upload_token = $('#csrf-token').get('.value');
var form_data = new FormData();
form_data.append('backup', file);
form_data.append('csrf_token', upload_token);
$.request('post', 'upload.php', form_data).then(
function success( result )
{
var ret = $.parseJSON( result );
if ( ret.token )
$('#csrf-token').set( { value: ret.token } );
if ( ret.ret && ret.ret == '0' ) // means that the process was launched
{
click_app($('#nc-restore'));
history.pushState(null, selectedID, "?app=" + selectedID);
$('#nc-restore-BACKUPFILE').set({ value: '/tmp/' + upload_token.replace('/', '') + file.name });
$('#nc-restore-config-button').trigger('click');
}
else
console.log('error uploading ' + file);
}
).error( errorMsg )
}
clicked_dialog_button = null;
clicked_dialog_action = null;
function dialog_action(button)
{
if ( clicked_dialog_action && clicked_dialog_button)
clicked_dialog_action(clicked_dialog_button);
}
// backups
function set_backup_handlers()
{
$( '.download-bkp' ).on('click', function(e)
{
var tr = this.up().up();
var path = tr.get('.id');
window.location.replace('download.php?bkp=' + encodeURIComponent(path) + '&token=' + encodeURIComponent(tr.next().get('.value')));
});
$( '.delete-bkp' ).on('click', function(e)
{
$('#confirmation-dialog').show();
clicked_dialog_action = del_bkp;
clicked_dialog_button = this;
});
$( '.restore-bkp' ).on('click', function(e)
{
$('#confirmation-dialog').show();
clicked_dialog_action = restore_bkp;
clicked_dialog_button = this;
});
$( '#restore-upload-btn' ).on('click', function(e)
{
var file = $$('#restore-upload').files[0];
if (!file) return;
$('#confirmation-dialog').show();
clicked_dialog_action = restore_upload;
clicked_dialog_button = this;
});
$( '.restore-snap' ).on('click', function(e)
{
$('#confirmation-dialog').show();
clicked_dialog_action = restore_snap;
clicked_dialog_button = this;
});
$( '.delete-snap' ).on('click', function(e)
{
$('#confirmation-dialog').show();
clicked_dialog_action = del_snap;
clicked_dialog_button = this;
});
}
function print_backups()
{
// request
$.request('post', 'ncp-launcher.php', { action:'backups',
csrf_token: $('#csrf-token-ui').get('.value') }
).then(
function success( result )
{
var ret = $.parseJSON( result );
if (ret.token)
$('#csrf-token-ui').set({ value: ret.token });
if (ret.ret && ret.ret == '0') {
$('#loading-backups-gif').hide();
$('#backups-content').ht(ret.output);
set_backup_handlers();
reload_sidebar();
}
}).error( errorMsg );
}
function reload_sidebar()
{
// request
@ -272,9 +436,16 @@ $(function()
{
if ( ret.ret == '0' )
{
if( ret.ref && ret.ref == 'nc-update' )
window.location.reload( true );
reload_sidebar();
if (ret.ref)
{
if (ret.ref == 'nc-update')
window.location.reload( true );
else if(ret.ref == 'nc-backup')
print_backups();
if(ret.ref != 'nc-restore' && ret.ref != 'nc-backup') // FIXME PHP is reloaded asynchronously after nc-restore
reload_sidebar();
}
$('.circle-retstatus').set('+icon-green-circle');
}
else
@ -354,7 +525,6 @@ $(function()
function dirname(path) { return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); }
var span = this.up().select('span', true);
console.log(span);
var path = dirname(this.get('.value'));
// request
@ -470,6 +640,22 @@ $(function()
$( '#first-run-wizard' ).hide();
} );
// dialog confirmation
$( '#confirmation-dialog-ok' ).on('click', function(e)
{
$( '#confirmation-dialog' ).hide();
dialog_action();
} );
$( '.confirmation-dialog-close' ).on('click', function(e)
{
$( '#confirmation-dialog' ).hide();
} );
$( '#confirmation-dialog' ).on('|click', function(e)
{
if( e.target.id == 'confirmation-dialog' )
$( '#confirmation-dialog' ).hide();
} );
// click to nextcloud button
$('#nextcloud-btn').set( '@href', window.location.protocol + '//' + window.location.hostname );
@ -482,6 +668,7 @@ $(function()
history.pushState(null, selectedID, "?app=dashboard");
} );
// TODO unify repeating code
// config button
$( '#config-btn' ).on('click', function(e)
{
@ -491,6 +678,15 @@ $(function()
history.pushState(null, selectedID, "?app=config");
} );
// backups button
$( '#backups-btn' ).on('click', function(e)
{
if ( lock ) return;
close_menu();
switch_to_section( 'backups' );
history.pushState(null, selectedID, "?app=backups");
} );
// language selection
var langold = $( '#language-selection' ).get( '.value' );
$( '#language-selection' ).on( 'change', function(e)

View File

@ -136,6 +136,21 @@ else if ( $_POST['action'] == "info" )
echo ' "ret": "' . $ret . '" }';
}
//
// backups
//
else if ( $_POST['action'] == "backups" )
{
ob_start();
include('backups.php');
$backups_page = ob_get_clean();
// return JSON
echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
echo ' "output": ' . json_encode($backups_page) . ' , ';
echo ' "ret": "0" }';
}
//
// sidebar
//
@ -175,6 +190,34 @@ else if ( $_POST['action'] == "path-exists" )
echo ' "ret": "' . $ret . '" }';
}
//
// del backup
//
else if ( $_POST['action'] == "del-bkp" )
{
$file = escapeshellarg($_POST['value']);
$ret = 1;
exec("sudo /home/www/ncp-backup-launcher.sh del $file", $out, $ret);
// return JSON
echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
echo ' "ret": "' . $ret . '" }';
}
//
// del snapshot
//
else if ( $_POST['action'] == "del-snap" )
{
$file = escapeshellarg($_POST['value']);
$ret = 1;
exec("sudo /home/www/ncp-backup-launcher.sh delsnp $file", $out, $ret);
// return JSON
echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
echo ' "ret": "' . $ret . '" }';
}
//
// poweroff
//

47
ncp-web/upload.php Normal file
View File

@ -0,0 +1,47 @@
<?php
///
// NextCloudPi Web Panel backend
//
// Copyleft 2019 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
// GPL licensed (see end of file) * Use at your own risk!
//
// More at https://nextcloudpi.com
///
include ('csrf.php');
session_start();
// CSRF check
$token = isset($_POST['csrf_token']) ? $_POST['csrf_token'] : '';
if ( empty($token) || !validateCSRFToken($token) )
exit( '{ "output": "Unauthorized request. Try reloading the page" }' );
isset($_FILES['backup']) or exit( '{ "output": "no upload" }' );
$error=$_FILES['backup']['error'];
if ($error !== 0)
exit( '{ "output": "upload error ' . $error . '" }' );
$file_name = $_POST['csrf_token'] . basename($_FILES['backup']['name']);
$file_name = str_replace('/', '', $file_name);
$file_size = $_FILES['backup']['size'];
$file_tmp = $_FILES['backup']['tmp_name'];
$file_type = $_FILES['backup']['type'];
preg_match( '/\.\./' , $file_name, $matches )
and exit( '{ "output": "Invalid input" , "token": "' . getCSRFToken() . '" }' );
if($file_size === 0)
$errors[]='No file';
$extension = pathinfo($file_name, PATHINFO_EXTENSION);
if ($extension !== "tar" and $extension !== "gz")
exit( '{ "output": "invalid file" }' );
if (!move_uploaded_file($file_tmp, sys_get_temp_dir() . '/' . $file_name))
exit('{ "output": "upload denied" }');
// return JSON
echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
echo ' "ret": "0" }';
?>

32
ncp.sh
View File

@ -22,7 +22,7 @@ install()
{
# NCP-CONFIG
apt-get update
$APTINSTALL git dialog whiptail jq
$APTINSTALL git dialog whiptail jq file
mkdir -p "$CONFDIR" "$BINDIR"
# include option in raspi-config (only Raspbian)
@ -131,11 +131,39 @@ EOF
cat > /home/www/ncp-launcher.sh <<'EOF'
#!/bin/bash
grep -q '[\\&#;`|*?~<>^()[{}$&[:space:]]' <<< "$*" && exit 1
source /usr/local/etc/library.sh
run_app $1
EOF
chmod 700 /home/www/ncp-launcher.sh
echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /sbin/halt, /sbin/reboot" >> /etc/sudoers
cat > /home/www/ncp-backup-launcher.sh <<'EOF'
#!/bin/bash
action="${1}"
file="${2}"
compressed="${3}"
grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
[[ "$file" =~ ".." ]] && exit 1
[[ "${action}" == "chksnp" ]] && {
btrfs subvolume show "$file" &>/dev/null || exit 1
exit
}
[[ "${action}" == "delsnp" ]] && {
btrfs subvolume delete "$file" || exit 1
exit
}
[[ -f "$file" ]] || exit 1
[[ "$file" =~ ".tar" ]] || exit 1
[[ "${action}" == "del" ]] && {
[[ "$(file "$file")" =~ "tar archive" ]] || [[ "$(file "$file")" =~ "gzip compressed data" ]] || exit 1
rm "$file" || exit 1
exit
}
[[ "$compressed" != "" ]] && pigz="-I pigz"
tar $pigz -tf "$file" data &>/dev/null
EOF
chmod 700 /home/www/ncp-backup-launcher.sh
echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /sbin/halt, /sbin/reboot" >> /etc/sudoers
# NCP AUTO TRUSTED DOMAIN
mkdir -p /usr/lib/systemd/system

View File

@ -27,6 +27,8 @@ nc-init
UFW
nc-snapshot
nc-snapshot-auto
nc-snapshot-sync
nc-restore-snapshot
nc-audit
nc-hdd-monitor
nc-zram
@ -187,6 +189,45 @@ EOF
# switch back to the apt LE version
which letsencrypt &>/dev/null || install_app letsencrypt
# update launchers
apt-get update
apt-get install -y --no-install-recommends file
cat > /home/www/ncp-launcher.sh <<'EOF'
#!/bin/bash
grep -q '[\\&#;`|*?~<>^()[{}$&[:space:]]' <<< "$*" && exit 1
source /usr/local/etc/library.sh
run_app $1
EOF
chmod 700 /home/www/ncp-launcher.sh
cat > /home/www/ncp-backup-launcher.sh <<'EOF'
#!/bin/bash
action="${1}"
file="${2}"
compressed="${3}"
grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
[[ "$file" =~ ".." ]] && exit 1
[[ "${action}" == "chksnp" ]] && {
btrfs subvolume show "$file" &>/dev/null || exit 1
exit
}
[[ "${action}" == "delsnp" ]] && {
btrfs subvolume delete "$file" || exit 1
exit
}
[[ -f "$file" ]] || exit 1
[[ "$file" =~ ".tar" ]] || exit 1
[[ "${action}" == "del" ]] && {
[[ "$(file "$file")" =~ "tar archive" ]] || [[ "$(file "$file")" =~ "gzip compressed data" ]] || exit 1
rm "$file" || exit 1
exit
}
[[ "$compressed" != "" ]] && pigz="-I pigz"
tar $pigz -tf "$file" data &>/dev/null
EOF
chmod 700 /home/www/ncp-backup-launcher.sh
sed -i 's|www-data ALL = NOPASSWD: .*|www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /sbin/halt, /sbin/reboot|' /etc/sudoers
# remove redundant opcache configuration. Leave until update bug is fixed -> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=815968
# Bug #416 reappeared after we moved to php7.2 and debian buster packages. (keep last)
[[ "$( ls -l /etc/php/7.2/fpm/conf.d/*-opcache.ini | wc -l )" -gt 1 ]] && rm "$( ls /etc/php/7.2/fpm/conf.d/*-opcache.ini | tail -1 )"