Merge pull request #15296 from uberbrady/expose_restore_sanitize

Expose the 'sanitize' system for backup restores to the web GUI
This commit is contained in:
snipe 2024-08-22 12:43:07 +01:00 committed by GitHub
commit ec0b9b198f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 107 additions and 87 deletions

View file

@ -32,6 +32,8 @@ DB_PREFIX=null
DB_DUMP_PATH='/usr/bin' DB_DUMP_PATH='/usr/bin'
DB_CHARSET=utf8mb4 DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci DB_COLLATION=utf8mb4_unicode_ci
DB_SANITIZE_BY_DEFAULT=false
# -------------------------------------------- # --------------------------------------------
# OPTIONAL: SSL DATABASE SETTINGS # OPTIONAL: SSL DATABASE SETTINGS

View file

@ -1204,7 +1204,7 @@ class SettingsController extends Controller
* @author [A. Gianotto] [<snipe@snipe.net>] * @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v6.0] * @since [v6.0]
*/ */
public function postRestore($filename = null) : RedirectResponse public function postRestore(Request $request, $filename = null): RedirectResponse
{ {
if (! config('app.lock_passwords')) { if (! config('app.lock_passwords')) {
@ -1224,13 +1224,29 @@ class SettingsController extends Controller
Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename); Log::debug('Attempting to restore from: '. storage_path($path).'/'.$filename);
// run the restore command $restore_params = [
Artisan::call('snipeit:restore',
[
'--force' => true, '--force' => true,
'--no-progress' => true, '--no-progress' => true,
'filename' => storage_path($path).'/'.$filename 'filename' => storage_path($path) . '/' . $filename
]); ];
if ($request->input('clean')) {
Log::debug("Attempting 'clean' - first, guessing prefix...");
Artisan::call('snipeit:restore', [
'--sanitize-guess-prefix' => true,
'filename' => storage_path($path) . '/' . $filename
]);
$guess_prefix_output = Artisan::output();
Log::debug("Sanitize output is: $guess_prefix_output");
list($prefix, $_output) = explode("\n", $guess_prefix_output);
Log::debug("prefix is: '$prefix'");
$restore_params['--sanitize-with-prefix'] = $prefix;
}
// run the restore command
Artisan::call('snipeit:restore',
$restore_params
);
// If it's greater than 300, it probably worked // If it's greater than 300, it probably worked
$output = Artisan::output(); $output = Artisan::output();

View file

@ -237,4 +237,6 @@ return [
], ],
], ],
'sanitize_by_default' => env('DB_SANITIZE_BY_DEFAULT', false),
]; ];

View file

@ -82,84 +82,45 @@ pieOptions = {
var baseUrl = $('meta[name="baseUrl"]').attr('content'); var baseUrl = $('meta[name="baseUrl"]').attr('content');
(function($, settings) { $(function () {
var Components = {};
Components.modals = {}; var $el = $('table');
// confirm restore modal // confirm restore modal
Components.modals.confirmRestore = function() {
var $el = $('table');
var events = { $el.on('click', '.restore-asset', function (evnt) {
'click': function(evnt) { var $context = $(this);
var $context = $(this); var $restoreConfirmModal = $('#restoreConfirmModal');
var $restoreConfirmModal = $('#restoreConfirmModal'); var href = $context.attr('href');
var href = $context.attr('href'); var message = $context.attr('data-content');
var message = $context.attr('data-content'); var title = $context.attr('data-title');
var title = $context.attr('data-title');
$('#restoreConfirmModalLabel').text(title); $('#confirmModalLabel').text(title);
$restoreConfirmModal.find('.modal-body').text(message); $restoreConfirmModal.find('.modal-body').text(message);
$('#restoreForm').attr('action', href); $('#restoreForm').attr('action', href);
$restoreConfirmModal.modal({ $restoreConfirmModal.modal({
show: true show: true
}); });
return false; return false;
} });
};
var render = function() {
$el.on('click', '.restore-asset', events['click']);
};
return {
render: render
};
};
// confirm delete modal // confirm delete modal
Components.modals.confirmDelete = function() {
var $el = $('table');
var events = { $el.on('click', '.delete-asset', function (evnt) {
'click': function(evnt) { var $context = $(this);
var $context = $(this); var $dataConfirmModal = $('#dataConfirmModal');
var $dataConfirmModal = $('#dataConfirmModal'); var href = $context.attr('href');
var href = $context.attr('href'); var message = $context.attr('data-content');
var message = $context.attr('data-content'); var title = $context.attr('data-title');
var title = $context.attr('data-title');
$('#myModalLabel').text(title); $('#myModalLabel').text(title);
$dataConfirmModal.find('.modal-body').text(message); $dataConfirmModal.find('.modal-body').text(message);
$('#deleteForm').attr('action', href); $('#deleteForm').attr('action', href);
$dataConfirmModal.modal({ $dataConfirmModal.modal({
show: true show: true
}); });
return false; return false;
}
};
var render = function() {
$el.on('click', '.delete-asset', events['click']);
};
return {
render: render
};
};
/**
* Application start point
* Component definition stays out of load event, execution only happens.
*/
$(function() {
new Components.modals.confirmRestore().render();
new Components.modals.confirmDelete().render();
}); });
}(jQuery, window.snipeit.settings));
$(document).ready(function () {
/* /*
* Slideout help menu * Slideout help menu

View file

@ -31,6 +31,8 @@ return [
'backups' => 'Backups', 'backups' => 'Backups',
'backups_help' => 'Create, download, and restore backups ', 'backups_help' => 'Create, download, and restore backups ',
'backups_restoring' => 'Restoring from Backup', 'backups_restoring' => 'Restoring from Backup',
'backups_clean' => 'Clean the backed-up database before restore',
'backups_clean_helptext' => "This can be useful if you're changing between database versions",
'backups_upload' => 'Upload Backup', 'backups_upload' => 'Upload Backup',
'backups_path' => 'Backups on the server are stored in <code>:path</code>', 'backups_path' => 'Backups on the server are stored in <code>:path</code>',
'backups_restore_warning' => 'Use the restore button <small><span class="btn btn-xs btn-warning"><i class="text-white fas fa-retweet" aria-hidden="true"></i></span></small> to restore from a previous backup. (This does not currently work with S3 file storage or Docker.)<br><br>Your <strong>entire :app_name database and any uploaded files will be completely replaced</strong> by what\'s in the backup file. ', 'backups_restore_warning' => 'Use the restore button <small><span class="btn btn-xs btn-warning"><i class="text-white fas fa-retweet" aria-hidden="true"></i></span></small> to restore from a previous backup. (This does not currently work with S3 file storage or Docker.)<br><br>Your <strong>entire :app_name database and any uploaded files will be completely replaced</strong> by what\'s in the backup file. ',

View file

@ -21,8 +21,36 @@
{{-- Page content --}} {{-- Page content --}}
@section('content') @section('content')
<div class="modal modal-warning fade" tabindex="-1" role="dialog" id="backupRestoreModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form method="post" role="form">
<div class="modal-header">
<h4 class="modal-title">Modal title</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>{{ trans('admin/settings/message.backup.restore_warning') }}</p>
<p><label><input type="checkbox"
name="clean" {{ config('backup.sanitize_by_default') ? "checked='checked'" : "" }}>{{ trans('admin/settings/general.backups_clean') }}
</label></p>
<p>{{ trans('admin/settings/general.backups_clean_helptext') }}</p>
</div>
<div class="modal-footer">
{{ csrf_field() }}
{{ method_field('POST') }}
<button type="button" class="btn btn-default pull-left"
data-dismiss="modal">{{ trans('general.cancel') }}</button>
<button type="submit" class="btn btn-outline">{{ trans('general.yes') }}</button>
</div>
</form>
</div>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-8">
@ -85,13 +113,12 @@
</a> </a>
@endif @endif
<a data-html="true" <a data-html="true"
href="{{ route('settings.backups.restore', $file['filename']) }}" href="{{ route('settings.backups.restore', $file['filename']) }}"
class="btn btn-warning btn-sm restore-asset {{ (config('app.lock_passwords')) ? ' disabled': '' }}" class="btn btn-warning btn-sm restore-backup {{ (config('app.lock_passwords')) ? ' disabled': '' }}"
data-toggle="modal" data-target="#backupRestoreModal"
data-content="{{ trans('admin/settings/message.backup.restore_warning') }}" data-title="{{ trans('admin/settings/message.backup.restore_confirm', array('filename' => e($file['filename']))) }}"
data-title="{{ trans('admin/settings/message.backup.restore_confirm', array('filename' => e($file['filename']))) }}" onClick="return false;">
onClick="return false;">
<i class="fas fa-retweet" aria-hidden="true"></i> <i class="fas fa-retweet" aria-hidden="true"></i>
<span class="sr-only">{{ trans('general.restore') }}</span> <span class="sr-only">{{ trans('general.restore') }}</span>
</a> </a>
@ -230,11 +257,21 @@
}); });
} }
}); });
}); });
// due to dynamic loading, we have to use the below 'weird' way of adding event handlers instead of just saying
// $('.restore-backup').on( .....
$('table').on('click', '.restore-backup', function (event) {
event.preventDefault();
var modal = $('#backupRestoreModal');
modal.find('.modal-title').text($(this).data('title'));
modal.find('form').attr('action', $(this).attr('href'));
modal.modal({
show: true
});
return false;
})
</script> </script>
@stop @stop