Skip to content

Commit

Permalink
Added DB Restore functionality to plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Saboteur777 committed Oct 26, 2020
1 parent fe7f30e commit ab5028d
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 8 deletions.
25 changes: 21 additions & 4 deletions src/controllers/BackupController.php
Expand Up @@ -47,10 +47,11 @@ class BackupController extends BaseController
// =========================================================================

/**
* Function that gets hit when a request is made to `/reporter/status`.
* Function that gets hit when a request is made to `/reporter/backup`.
*
* @return array|false|string
* @throws NotFoundHttpException
* @throws \craft\errors\ShellCommandException
* @throws \yii\base\Exception
*/
public function actionIndex()
{
Expand All @@ -60,8 +61,24 @@ public function actionIndex()
$this->checkIfAuthenticated();

$backupService = new BackupService();
$backupFile = $backupService->createDbBackup();
$backupService->createDbBackup();
}

/**
*
*
* @return bool
* @throws \yii\web\BadRequestHttpException
* @throws \yii\web\ForbiddenHttpException
*/
public function actionRestore()
{
$this->requirePostRequest();
$this->requireCpRequest();
$this->requirePermission('craft-reporter:restore-utility');

$backupService = new BackupService();

return Craft::$app->getResponse()->sendFile($backupFile);
return $backupService->restoreDbBackup();
}
}
24 changes: 24 additions & 0 deletions src/helpers/FilenameHelper.php
@@ -0,0 +1,24 @@
<?php
/**
* Reporter plugin for Craft CMS 3.x
*
* Reporter plugin for Craft CMS.
*
* @link https://www.webmenedzser.hu
* @copyright Copyright (c) 2020 Ottó Radics
*/

namespace webmenedzser\reporter\helpers;

/**
* @author Ottó Radics
* @package Reporter
* @since 1.10.0
*/
class FilenameHelper
{
public static function getFilename($prefix, $slug, $extension)
{
return $prefix . '-' . $slug . '-' . date('Ymd-His') . '.' . $extension;
}
}
123 changes: 119 additions & 4 deletions src/services/BackupService.php
Expand Up @@ -11,10 +11,15 @@
namespace webmenedzser\reporter\services;

use webmenedzser\reporter\Reporter;
use webmenedzser\reporter\helpers\FilenameHelper;

use Craft;
use craft\base\Component;

use \Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use ZipArchive;

/**
Expand All @@ -24,7 +29,11 @@
*/
class BackupService extends Component
{
public function createDbBackup() : string
/**
* @throws \craft\errors\ShellCommandException
* @throws \yii\base\Exception
*/
public function createDbBackup()
{
$backupPath = Craft::$app->db->backup();

Expand All @@ -39,7 +48,7 @@ public function createDbBackup() : string
*/
$zip = new ZipArchive();
$zipPath = $backupPath . '.zip';
if ($zip->open($zipPath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) !== TRUE) {
if ($zip->open($zipPath, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) !== true) {
die ("An error occurred when creating ZIP file.");
}

Expand All @@ -58,10 +67,116 @@ public function createDbBackup() : string
$zip->close();

/**
* Delete the uncompressed backup.
* Stream file to client.
*/
Craft::$app->getResponse()->sendFile($zipPath);

/**
* Delete the files to prevent unnecessary storage use.
*/
unlink($backupPath);
unlink($zipPath);
}

/**
* @return bool
* @throws \Exception
*/
public function restoreDbBackup()
{
$location = Craft::$app->getPath()->getDbBackupPath();
$zipPath = $this->_downloadBackup($location);
$backupFilename = $this->_extractBackup($zipPath);
$backupPath = $location . DIRECTORY_SEPARATOR . $backupFilename;

if (!is_file($backupPath)) {
throw new FileNotFoundException();
}

try {
Craft::$app->getDb()->restore($backupPath);
} catch (\Throwable $e) {
Craft::$app->getErrorHandler()->logException($e);

return false;
}

unlink($backupPath);

return $zipPath;
return true;
}

/**
* Download DB Backup from Craft Report.
*
* @param String $location
*
* @return false|string
* @throws \GuzzleHttp\Exception\GuzzleException
*/
private function _downloadBackup(String $location)
{
$key = Craft::parseEnv(Reporter::$plugin->getSettings()->apiKey);
if (!$key) {
throw new \Exception('Craft Report API Key is not set.');
}

$client = new Client();
$filename = FilenameHelper::getFilename('db-backup', $key, 'zip');
$path = $location . DIRECTORY_SEPARATOR . $filename;

$client->request(
'POST',
'https://craft.report/api/v1/restore',
[
'sink' => $path,
'form_params' => [
'key' => $key
]
]
);


return $path;
}

/**
* Extract archive on $path.
*
* @param String $zipPath
*
* @return false|string
* @throws \yii\base\Exception
*/
private function _extractBackup(String $zipPath) : String
{
/**
* Open and extract archive.
*/
$zipArchive = new ZipArchive();
if ($zipArchive->open($zipPath) !== true) {
throw new Exception('Failed extracting ZIP archive.');
}

/**
* Set backupEncryptionKey if it is set in plugin settings.
*/
$backupEncryptionKey = Craft::parseEnv(Reporter::$plugin->getSettings()->backupEncryptionKey) ?? null;
if ($backupEncryptionKey) {

$zipArchive->setPassword($backupEncryptionKey);
}

$backupFilename = $zipArchive->getNameIndex(0);

$zipArchive->extractTo(Craft::$app->getPath()->getDbBackupPath());
$zipArchive->close();

/**
* Delete archive.
*/
unlink($zipPath);

return $backupFilename;
}
}

0 comments on commit ab5028d

Please sign in to comment.