Ver Fonte

added organizr backup function

CauseFX há 5 anos atrás
pai
commit
69d7605a78

+ 64 - 2
api/functions/backup-functions.php

@@ -15,7 +15,44 @@ trait BackupFunctions
 		}
 	}
 	
-	public function backupDB($type = 'config')
+	public function deleteBackup($filename)
+	{
+		$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+		$path = $this->config['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
+		$filename = $path . $filename;
+		if ($ext == 'zip') {
+			if (file_exists($filename)) {
+				$this->writeLog('success', 'Backup Manager Function -  Deleted Backup [' . pathinfo($filename, PATHINFO_BASENAME) . ']', $this->user['username']);
+				$this->setAPIResponse(null, pathinfo($filename, PATHINFO_BASENAME) . ' has been deleted', null);
+				return (unlink($filename));
+			} else {
+				$this->setAPIResponse('error', 'File does not exist', 404);
+				return false;
+			}
+		} else {
+			$this->setAPIResponse('error', pathinfo($filename, PATHINFO_BASENAME) . ' is not approved to be deleted', 409);
+			return false;
+		}
+	}
+	
+	public function downloadBackup($filename)
+	{
+		$path = $this->config['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
+		$filename = $path . $filename;
+		if (file_exists($filename)) {
+			header('Content-Type: application/zip');
+			header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
+			header('Content-Length: ' . filesize($filename));
+			flush();
+			readfile($filename);
+			exit();
+		} else {
+			$this->setAPIResponse('error', 'File does not exist', 404);
+			return false;
+		}
+	}
+	
+	public function backupOrganizr($type = 'config')
 	{
 		$directory = $this->config['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
 		@mkdir($directory, 0770, true);
@@ -44,8 +81,10 @@ trait BackupFunctions
 			}
 			$zip->close();
 			$this->writeLog('success', 'BACKUP: backup process finished', 'SYSTEM');
+			$this->setAPIResponse('success', 'Backup has been created', 200);
 			return true;
 		} else {
+			$this->setAPIResponse('error', 'Backup creation failed', 409);
 			return false;
 		}
 		
@@ -56,7 +95,30 @@ trait BackupFunctions
 		$path = $this->config['dbLocation'] . 'backups' . DIRECTORY_SEPARATOR;
 		@mkdir($path, 0770, true);
 		$files = array_diff(scandir($path), array('.', '..'));
-		return array_reverse($files);
+		$fileList = [];
+		$totalFiles = 0;
+		$totalFileSize = 0;
+		foreach ($files as $file) {
+			if (file_exists($path . $file)) {
+				$size = filesize($path . $file);
+				$totalFileSize = $totalFileSize + $size;
+				$totalFiles = $totalFiles + 1;
+				try {
+					$fileList['files'][] = [
+						'name' => $file,
+						'size' => $this->human_filesize($size, 0),
+						'date' => gmdate("Y-m-d\TH:i:s\Z", (filemtime($path . $file)))
+					];
+				} catch (Exception $e) {
+					$this->setAPIResponse('error', 'Backup list failed', 409, $e->getMessage());
+					return false;
+				}
+			}
+		}
+		$fileList['total_files'] = $totalFiles;
+		$fileList['total_size'] = $this->human_filesize($totalFileSize, 2);
+		$this->setAPIResponse('success', null, 200, array_reverse($fileList));
+		return array_reverse($fileList);
 	}
 	
 }

+ 8 - 0
api/functions/normal-functions.php

@@ -538,6 +538,14 @@ trait NormalFunctions
 		}
 		return $isLocal;
 	}
+	
+	public function human_filesize($bytes, $dec = 2)
+	{
+		$bytes = number_format($bytes, 0, '.', '');
+		$size = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
+		$factor = floor((strlen($bytes) - 1) / 3);
+		return sprintf("%.{$dec}f %s", $bytes / (1024 ** $factor), $size[$factor]);
+	}
 }
 
 // Leave for deluge class

+ 55 - 0
api/pages/settings-settings-backup.php

@@ -0,0 +1,55 @@
+<?php
+$GLOBALS['organizrPages'][] = 'settings_settings_backup';
+function get_page_settings_settings_backup($Organizr)
+{
+	if (!$Organizr) {
+		$Organizr = new Organizr();
+	}
+	if ((!$Organizr->hasDB())) {
+		return false;
+	}
+	if (!$Organizr->qualifyRequest(1, true)) {
+		return false;
+	}
+	return '
+    <script>
+		getOrganizrBackups();
+    </script>
+ 
+    <div class="white-box bg-org">
+		<div class="col-md-3 col-sm-4 col-xs-6 pull-right">
+			<button onclick="createOrganizrBackup()" class="btn btn-sm btn-info btn-rounded waves-effect waves-light pull-right" type="button"><span class="btn-label"><i class="fa ti-export"></i></span><span lang="en">Create Backup</span></button>
+		</div>
+		<h3 class="box-title" lang="en">Backup Organizr</h3>
+		<div class="row sales-report">
+			<div class="col-md-6 col-sm-6 col-xs-6">
+				<h2 id="backup-total-files"><i class="fa fa-spin fa-spinner"></i></h2>
+				<p lang="en">Files</p>
+			</div>
+			<div class="col-md-6 col-sm-6 col-xs-6 ">
+				<h1 class="text-right text-info m-t-20" id="backup-total-size"><i class="fa fa-spin fa-spinner"></i></h1>
+			</div>
+		</div>
+		<div class="table-responsive">
+			<table class="table">
+				<thead>
+					<tr>
+						<th>#</th>
+						<th lang="en">Name</th>
+						<th lang="en">Type</th>
+						<th lang="en">Version</th>
+						<th lang="en">Date</th>
+						<th lang="en">Action</th>
+					</tr>
+				</thead>
+				<tbody id="backup-file-list">
+					<tr>
+						<td class="text-center" colspan="6"><i class="fa fa-spin fa-spinner"></i></td>
+					</tr>
+				</tbody>
+			</table>
+		</div>
+	</div>
+    <!-- /.container-fluid -->
+    ';
+}

+ 6 - 0
api/pages/settings.php

@@ -177,6 +177,8 @@ function get_page_settings($Organizr)
                             </li>
                             <li onclick="changeSettingsMenu(\'Settings::System Settings::Updates\')" role="presentation" class=""><a id="update-button" href="#settings-settings-updates" aria-controls="profile" role="tab" data-toggle="tab" aria-expanded="false"><span class="visible-xs"><i class="ti-package"></i></span> <span class="hidden-xs" lang="en">Updates</span></a>
                             </li>
+                            <li onclick="changeSettingsMenu(\'Settings::System Settings::Backup\');loadSettingsPage2(\'api/v2/page/settings_settings_backup\',\'#settings-settings-backup\',\'Backup\');" role="presentation" class=""><a id="settings-settings-backup-anchor" href="#settings-settings-backup" aria-controls="home" role="tab" data-toggle="tab" aria-expanded="false"><span class="visible-xs"><i class="ti-export"></i></span><span class="hidden-xs" lang="en">Backup</span></a>
+                            </li>
                             <li onclick="changeSettingsMenu(\'Settings::System Settings::Donate\')" role="presentation" class=""><a id="settings-settings-donate-anchor" href="#settings-settings-donate" aria-controls="profile" role="tab" data-toggle="tab" aria-expanded="false"><span class="visible-xs"><i class="ti-money"></i></span> <span class="hidden-xs" lang="en">Donate</span></a>
                             </li>
                         </ul>
@@ -194,6 +196,10 @@ function get_page_settings($Organizr)
                                 <h2 lang="en">Loading...</h2>
                                 <div class="clearfix"></div>
                             </div>
+                            <div role="tabpanel" class="tab-pane fade" id="settings-settings-backup">
+                                <h2 lang="en">Loading...</h2>
+                                <div class="clearfix"></div>
+                            </div>
                             <div role="tabpanel" class="tab-pane fade active in" id="settings-settings-about">
                             	<div class="row">
 	                                <div class="col-lg-12">

+ 41 - 0
api/v2/routes/backup.php

@@ -0,0 +1,41 @@
+<?php
+$app->get('/backup', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->getBackups();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->post('/backup', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->backupOrganizr();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/backup/{filename}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->downloadBackup($args['filename']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/backup/{filename}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->deleteBackup($args['filename']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 27 - 0
js/custom.js

@@ -1595,6 +1595,33 @@ $(document).on("click", ".purgeLog", function () {
     }
 
 });
+$(document).on("click", ".delete-backup", function () {
+	$('#settings-settings-backup').block({
+		message: '<p style="margin:0;padding:8px;font-size:24px;" lang="en">Deleting Backup...</p>',
+		css: {
+			color: '#fff',
+			border: '1px solid #5761a9',
+			backgroundColor: '#707cd2'
+		}
+	});
+	let filename = $(this).attr('data-file');
+	if(filename !== ''){
+		let post = {
+			api:'api/v2/backup/' + filename,
+			messageTitle:'',
+			messageBody:window.lang.translate('Deleted Backup')+': '+filename,
+			error:'Organizr Function: Backup API Connection Failed'
+		};
+		organizrAPI2('DELETE',post.api,'',true).success(function(data) {
+			message(post.messageTitle,post.messageBody,activeInfo.settings.notifications.position,"#FFF","success","5000");
+			getOrganizrBackups();
+			$('#settings-settings-backup').unblock();
+		}).fail(function(xhr) {
+			console.error(post.error);
+			$('#settings-settings-backup').unblock();
+		});
+	}
+});
 //Show Password
 $(document).on("click", ".showPassword", function () {
     var toggle = $(this).parent().parent().find('.password-alt');

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
js/custom.min.js


+ 65 - 0
js/functions.js

@@ -3660,6 +3660,71 @@ function themeAnalytics(theme_name){
         }
     });
 }
+function getOrganizrBackups(){
+	organizrAPI2('GET','api/v2/backup').success(function(data) {
+		try {
+			let json = data.response;
+			$('#backup-file-list').html(buildOrganizrBackups(json.data));
+		}catch(e) {
+			console.log(e + ' error: ' + data);
+			orgErrorAlert('<h4>' + e + '</h4>' + formatDebug(data));
+			return false;
+		}
+	}).fail(function(xhr) {
+		console.error("Organizr Function: API Connection Failed");
+	});
+}
+function createOrganizrBackup(){
+	$('#settings-settings-backup').block({
+		message: '<p style="margin:0;padding:8px;font-size:24px;" lang="en">Backing up...</p>',
+		css: {
+			color: '#fff',
+			border: '1px solid #5761a9',
+			backgroundColor: '#707cd2'
+		}
+	});
+	organizrAPI2('POST','api/v2/backup',{}).success(function(data) {
+		try {
+			let response = data.response;
+			if(response){
+				getOrganizrBackups();
+			}
+		}catch(e) {
+			console.log(e + ' error: ' + data);
+			orgErrorAlert('<h4>' + e + '</h4>' + formatDebug(data));
+			return false;
+		}
+		$('#settings-settings-backup').unblock();
+	}).fail(function(xhr) {
+		$('#settings-settings-backup').unblock();
+		console.error("Organizr Function: API Connection Failed | Error: " + xhr.responseJSON.response.message);
+	});
+}
+function buildOrganizrBackups(array){
+	let list =  '';
+	if(array.total_files > 0) {
+		$.each(array.files, function (i, v) {
+			i++;
+			let pattern = /\[[^\]]*\]/mg;
+			let version = (typeof v.name.match(pattern)[1] !== 'undefined') ?  v.name.match(pattern)[1] : 'N/A';
+			list += `
+			<tr>
+				<td>` + i + `</td>
+				<td class="txt-oflo">` + v.name + `</td>
+				<td><span class="label label-primary label-rouded">` + version + `</span> </td>
+				<td class="txt-oflo">` + v.size + `</td>
+				<td><span class="text-info tooltip-info" data-toggle="tooltip" data-placement="right" title="" data-original-title="`+moment(v.date).format('LLL')+`">`+moment.utc(v.date, "YYYY-MM-DD hh:mm[Z]").local().fromNow()+`</span></td>
+				<td><span class="text-primary"><a href="api/v2/backup/`+v.name+`"><i class="fa fa-download download-backup" data-file="` + v.name + `"></i></a> | <a href="javascript:void(0)"><i class="fa fa-trash-o delete-backup" data-file="` + v.name + `"></i></a></span></td>
+			</tr>
+			`;
+		});
+	}else{
+		list = '<tr><td class="text-center" colspan="6">No Backups made yet</td></tr>';
+	}
+	$('#backup-total-files').html(array.total_files);
+	$('#backup-total-size').html(array.total_size);
+	return list;
+}
 function updateBar(){
 	return `
 	<div class="white-box m-0">

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff