FEATURE: File Backup
Turned on file backups that grabs uploaded images in the public directory and saves them in a zip so they can be downloaded and archived.
This commit is contained in:
parent
bc7b1fe7ec
commit
4e880092c1
8 changed files with 144 additions and 46 deletions
|
@ -58,15 +58,36 @@ class SettingsAPIController extends Controller
|
||||||
|
|
||||||
public function createBackup(Request $request)
|
public function createBackup(Request $request)
|
||||||
{
|
{
|
||||||
return response()->json($this->maintenance->createBackup())->header('Content-Type', 'application/json');
|
$body = json_decode($request->getContent());
|
||||||
|
if ($body->task == 'content_backup') {
|
||||||
|
return response()->json(
|
||||||
|
$this->maintenance->createContentBackUp()
|
||||||
|
)->header('Content-Type', 'application/json');
|
||||||
|
} else {
|
||||||
|
return response()->json(
|
||||||
|
$this->maintenance->createFileBackUp()
|
||||||
|
)->header('Content-Type', 'application/json');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function downloadBackup(Request $request)
|
public function downloadBackup(Request $request)
|
||||||
{
|
{
|
||||||
if ($this->member::status()) {
|
if ($this->member::status()) {
|
||||||
$latest = $this->settings->getGlobal()['last_backup'];
|
$latest = '';
|
||||||
$file = 'backup-' . $latest . '.zip';
|
$file = '';
|
||||||
return response()->download('../content/backups/' . $file, $file, ['Content-Type: application/zip']);
|
if (explode('/', $request->getRequestUri())[4] == 'content-download') {
|
||||||
|
$latest = $this->settings->getGlobal()['last_content_backup'];
|
||||||
|
$file = 'backup-content-' . $latest . '.zip';
|
||||||
|
} else {
|
||||||
|
$latest = $this->settings->getGlobal()['last_files_backup'];
|
||||||
|
$file = 'backup-files-' . $latest . '.zip';
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->download(
|
||||||
|
'../content/backups/' . $file,
|
||||||
|
$file,
|
||||||
|
['Content-Type: application/zip']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -267,7 +267,8 @@ class SortingService
|
||||||
public function settings()
|
public function settings()
|
||||||
{
|
{
|
||||||
$global = $this->settings->getGlobal();
|
$global = $this->settings->getGlobal();
|
||||||
$updated = new Carbon($global['last_backup']);
|
$updatedContent = new Carbon($global['last_content_backup']);
|
||||||
|
$updatedFiles = new Carbon($global['last_files_backup']);
|
||||||
$status = session('member') != '' ? true : false;
|
$status = session('member') != '' ? true : false;
|
||||||
$pageOptions = [
|
$pageOptions = [
|
||||||
'title' => 'Settings',
|
'title' => 'Settings',
|
||||||
|
@ -279,7 +280,8 @@ class SortingService
|
||||||
'siteTitle' => $global['title'],
|
'siteTitle' => $global['title'],
|
||||||
'baseUrl' => $global['base_url'],
|
'baseUrl' => $global['base_url'],
|
||||||
'desc' => $global['descriptions'],
|
'desc' => $global['descriptions'],
|
||||||
'lastBackup' => $updated->format('Y F d H i s'),
|
'lastContentBackup' => $updatedContent->format('Y F d H i s'),
|
||||||
|
'lastFilesBackup' => $updatedFiles->format('Y F d H i s'),
|
||||||
'currentTheme' => $global['theme'],
|
'currentTheme' => $global['theme'],
|
||||||
'themes' => $this->themes->getThemes(),
|
'themes' => $this->themes->getThemes(),
|
||||||
'apiStatus' => isset($global['externalAPI']) ? $global['externalAPI'] : 'false',
|
'apiStatus' => isset($global['externalAPI']) ? $global['externalAPI'] : 'false',
|
||||||
|
|
|
@ -14,7 +14,7 @@ class MaintenanceService
|
||||||
$this->settings = $settingsService;
|
$this->settings = $settingsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createBackUp()
|
public function createContentBackUp()
|
||||||
{
|
{
|
||||||
//make sure back directory is there
|
//make sure back directory is there
|
||||||
$stamp = Carbon::now()->format("YmdGis");
|
$stamp = Carbon::now()->format("YmdGis");
|
||||||
|
@ -25,7 +25,7 @@ class MaintenanceService
|
||||||
$zip = new \ZipArchive();
|
$zip = new \ZipArchive();
|
||||||
|
|
||||||
$zip->open(
|
$zip->open(
|
||||||
env('FIPAMO_BACKUPS') . '/backup-' . $stamp . '.zip',
|
env('FIPAMO_BACKUPS') . '/backup-content-' . $stamp . '.zip',
|
||||||
\ZipArchive::CREATE | \ZipArchive::OVERWRITE
|
\ZipArchive::CREATE | \ZipArchive::OVERWRITE
|
||||||
);
|
);
|
||||||
//gather data and path info for md pages
|
//gather data and path info for md pages
|
||||||
|
@ -63,29 +63,36 @@ class MaintenanceService
|
||||||
|
|
||||||
//gather paths for user images
|
//gather paths for user images
|
||||||
$userImages = [];
|
$userImages = [];
|
||||||
|
if (is_dir('../public/assets/images/user')) {
|
||||||
$dir = new \RecursiveDirectoryIterator('../public/assets/images/user');
|
$dir = new \RecursiveDirectoryIterator('../public/assets/images/user');
|
||||||
$flat = new \RecursiveIteratorIterator($dir);
|
$flat = new \RecursiveIteratorIterator($dir);
|
||||||
$files = new \RegexIterator($flat, '/\.png|jpg|gif$/i');
|
$files = new \RegexIterator($flat, '/\.png|jpg|gif$/i');
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
$userImages[] = ['path' => $file->getPath(), 'file' => $file->getFilename()];
|
$userImages[] = ['path' => $file->getPath(), 'file' => $file->getFilename()];
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
//gather paths for blog documents
|
//gather paths for blog documents
|
||||||
$blogDocs = [];
|
$blogDocs = [];
|
||||||
|
if (is_dir('../public/assets/docs/blog')) {
|
||||||
$dir = new \RecursiveDirectoryIterator('../public/assets/docs/blog');
|
$dir = new \RecursiveDirectoryIterator('../public/assets/docs/blog');
|
||||||
$flat = new \RecursiveIteratorIterator($dir);
|
$flat = new \RecursiveIteratorIterator($dir);
|
||||||
$files = new \RegexIterator($flat, '/\.txt|pdf|rtf$/i');
|
$files = new \RegexIterator($flat, '/\.txt|pdf|rtf$/i');
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
$blogDocs[] = ['path' => $file->getPath(), 'file' => $file->getFilename()];
|
$blogDocs[] = ['path' => $file->getPath(), 'file' => $file->getFilename()];
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
//gather paths for blog videos
|
//gather paths for blog videos
|
||||||
$blogVids = [];
|
$blogVids = [];
|
||||||
|
if (is_dir('../public/assets/video/blog')) {
|
||||||
$dir = new \RecursiveDirectoryIterator('../public/assets/video/blog');
|
$dir = new \RecursiveDirectoryIterator('../public/assets/video/blog');
|
||||||
$flat = new \RecursiveIteratorIterator($dir);
|
$flat = new \RecursiveIteratorIterator($dir);
|
||||||
$files = new \RegexIterator($flat, '/\.mp4$/i');
|
$files = new \RegexIterator($flat, '/\.mp4$/i');
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
$blogVids[] = ['path' => $file->getPath(), 'file' => $file->getFilename()];
|
$blogVids[] = ['path' => $file->getPath(), 'file' => $file->getFilename()];
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
//add directory for settings and save them
|
//add directory for settings and save them
|
||||||
$zip->addEmptyDir('config');
|
$zip->addEmptyDir('config');
|
||||||
|
@ -112,8 +119,55 @@ class MaintenanceService
|
||||||
unlink(env('FIPAMO_BACKUPS') . '/blog_vids_temp.json');
|
unlink(env('FIPAMO_BACKUPS') . '/blog_vids_temp.json');
|
||||||
|
|
||||||
//update settings file with latest back up date
|
//update settings file with latest back up date
|
||||||
$this->settings->updateGlobalData('last_backup', $stamp);
|
$this->settings->updateGlobalData('last_content_backup', $stamp);
|
||||||
|
|
||||||
return ['message' => "Backup created. THIS IS A SAFE SPACE!"];
|
return ['message' => "Content backup created. THIS IS A SAFE SPACE!"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createFileBackUp()
|
||||||
|
{
|
||||||
|
$stamp = Carbon::now()->format("YmdGis");
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
$zip->open(
|
||||||
|
env('FIPAMO_BACKUPS') . '/backup-files-' . $stamp . '.zip',
|
||||||
|
\ZipArchive::CREATE | \ZipArchive::OVERWRITE
|
||||||
|
);
|
||||||
|
//gather data and path info for blog images
|
||||||
|
$blogImagesPath = '../public/assets/images/blog';
|
||||||
|
$yearPaths = glob($blogImagesPath . '/*', GLOB_ONLYDIR);
|
||||||
|
foreach ($yearPaths as $years) {
|
||||||
|
$year = explode('/', $years);
|
||||||
|
$monthsPath = glob($blogImagesPath . '/' . $year[5] . '/*', GLOB_ONLYDIR);
|
||||||
|
foreach ($monthsPath as $months) {
|
||||||
|
$month = explode('/', $months);
|
||||||
|
//once info is collected, add images pages to zip
|
||||||
|
$options = [
|
||||||
|
'add_path' => 'public/assets/images/blog/' . $year[5] . '/' . $month[6] . '/',
|
||||||
|
'remove_all_path' => true,
|
||||||
|
];
|
||||||
|
$zip->addGlob($months . '/*.*', GLOB_BRACE, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//gather data and path info for user images
|
||||||
|
$userImagesPath = '../public/assets/images/user';
|
||||||
|
$yearPaths = glob($userImagesPath . '/*', GLOB_ONLYDIR);
|
||||||
|
foreach ($yearPaths as $years) {
|
||||||
|
$year = explode('/', $years);
|
||||||
|
$monthsPath = glob($userImagesPath . '/' . $year[5] . '/*', GLOB_ONLYDIR);
|
||||||
|
foreach ($monthsPath as $months) {
|
||||||
|
$month = explode('/', $months);
|
||||||
|
//once info is collected, add images pages to zip
|
||||||
|
$options = [
|
||||||
|
'add_path' => 'public/assets/images/user/' . $year[5] . '/' . $month[6] . '/',
|
||||||
|
'remove_all_path' => true,
|
||||||
|
];
|
||||||
|
$zip->addGlob($months . '/*.*', GLOB_BRACE, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$zip->close();
|
||||||
|
$this->settings->updateGlobalData('last_files_backup', $stamp);
|
||||||
|
return ['message' => "Files are backed up. Breath Easy!"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
"renderOnSave": "false",
|
"renderOnSave": "false",
|
||||||
"theme": "fipamo-default-v2",
|
"theme": "fipamo-default-v2",
|
||||||
"display_limit": 5,
|
"display_limit": 5,
|
||||||
"last_backup": null,
|
"last_content_backup": null,
|
||||||
|
"last_file_backup": null,
|
||||||
"externalAPI": "false",
|
"externalAPI": "false",
|
||||||
"dynamicRender": "false"
|
"dynamicRender": "false"
|
||||||
},
|
},
|
||||||
|
|
|
@ -151,7 +151,7 @@ class MaintenanceManager {
|
||||||
* Promise method for creating a zip back up of current site. For local use only.
|
* Promise method for creating a zip back up of current site. For local use only.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
backup() {
|
backup(backup_task) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var url, event, method, type, data;
|
var url, event, method, type, data;
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ class MaintenanceManager {
|
||||||
event = TASK_BACKUP_CREATE;
|
event = TASK_BACKUP_CREATE;
|
||||||
method = REQUEST_TYPE_PUT;
|
method = REQUEST_TYPE_PUT;
|
||||||
type = CONTENT_TYPE_JSON;
|
type = CONTENT_TYPE_JSON;
|
||||||
data = { task: 'create_backup' };
|
data = backup_task;
|
||||||
this._request(url, null, event, method, type, data)
|
this._request(url, null, event, method, type, data)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
|
|
|
@ -156,7 +156,11 @@ export default class SettingsIndex {
|
||||||
}
|
}
|
||||||
//handle backup from settings
|
//handle backup from settings
|
||||||
document
|
document
|
||||||
.getElementById('create-backup')
|
.getElementById('create-content-backup')
|
||||||
|
.addEventListener('click', e => this.handleBackup(e));
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById('create-file-backup')
|
||||||
.addEventListener('click', e => this.handleBackup(e));
|
.addEventListener('click', e => this.handleBackup(e));
|
||||||
}
|
}
|
||||||
//--------------------------
|
//--------------------------
|
||||||
|
@ -259,9 +263,19 @@ export default class SettingsIndex {
|
||||||
handleBackup(e) {
|
handleBackup(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
notify.alert('Creating backup', null);
|
let id = '';
|
||||||
|
let type = '';
|
||||||
|
|
||||||
|
e.target.id == '' ? (id = e.target.parentNode.id) : (id = e.target.id);
|
||||||
|
if (id == 'create-content-backup') {
|
||||||
|
notify.alert('Creating Content Backup', null);
|
||||||
|
type = { task: 'content_backup' };
|
||||||
|
} else {
|
||||||
|
notify.alert('Creating File Backup', null);
|
||||||
|
type = { task: 'file_backup' };
|
||||||
|
}
|
||||||
this.mm
|
this.mm
|
||||||
.backup()
|
.backup(type)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
notify.alert(r.message, true);
|
notify.alert(r.message, true);
|
||||||
})
|
})
|
||||||
|
|
|
@ -94,19 +94,18 @@
|
||||||
<svg id="nav-menu-icon" class="icon">
|
<svg id="nav-menu-icon" class="icon">
|
||||||
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-copy"/>
|
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-copy"/>
|
||||||
</svg>
|
</svg>
|
||||||
<button id="create-backup">
|
<button id="create-content-backup">
|
||||||
<span>CONTENT BACKUP</span>
|
<span>CONTENT BACKUP</span>
|
||||||
</button>
|
</button>
|
||||||
<span>
|
<span>
|
||||||
@if($lastBackup != '')
|
@if($lastContentBackup != '')
|
||||||
LAST BACK UP
|
MOST RECENT:
|
||||||
<a href="/api/v1/backup/download">{{ $lastBackup }}</a><br/>
|
<a href="/api/v1/backup/content-download">{{ $lastContentBackup }}</a><br/>
|
||||||
@else
|
@else
|
||||||
<span>span No back ups. Frowny face.</span>
|
<span>span No back ups. Frowny face.</span>
|
||||||
@endif
|
@endif
|
||||||
<span>
|
<span>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO: File Back up option
|
|
||||||
<div class="option-container">
|
<div class="option-container">
|
||||||
<svg id="nav-menu-icon" class="icon">
|
<svg id="nav-menu-icon" class="icon">
|
||||||
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-images"/>
|
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-images"/>
|
||||||
|
@ -114,9 +113,15 @@
|
||||||
<button id="create-file-backup">
|
<button id="create-file-backup">
|
||||||
<span>FILE BACKUP</span>
|
<span>FILE BACKUP</span>
|
||||||
</button>
|
</button>
|
||||||
<span>COMING SOON</span>
|
<span>
|
||||||
|
@if($lastFilesBackup != '')
|
||||||
|
MOST RECENT:
|
||||||
|
<a href="/api/v1/backup/files-download">{{ $lastFilesBackup }}</a><br/>
|
||||||
|
@else
|
||||||
|
<span>span No back ups. Frowny face.</span>
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
<div class="option-container">
|
<div class="option-container">
|
||||||
<svg id="nav-menu-icon" class="icon">
|
<svg id="nav-menu-icon" class="icon">
|
||||||
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-back-in-time"/>
|
<use id="nav-menu-icon" xlink:href="/assets/images/global/sprite.svg#entypo-back-in-time"/>
|
||||||
|
@ -124,7 +129,7 @@
|
||||||
<button id="reset-to-default">
|
<button id="reset-to-default">
|
||||||
<span>RESET TO DEFAULT</span>
|
<span>RESET TO DEFAULT</span>
|
||||||
</button>
|
</button>
|
||||||
<span>Restores site to default state. !CANNOT UNDO!</span>
|
<span>Deletes all content and configs <strong>CANNOT UNDO</strong></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -31,7 +31,8 @@ Route::put("/v1/settings/publish", [SettingsAPIController::class, 'publish']);
|
||||||
Route::put("/v1/settings/sync", [SettingsAPIController::class, 'sync']);
|
Route::put("/v1/settings/sync", [SettingsAPIController::class, 'sync']);
|
||||||
Route::put("/v1/settings/nav-sync", [SettingsAPIController::class, 'navSync']);
|
Route::put("/v1/settings/nav-sync", [SettingsAPIController::class, 'navSync']);
|
||||||
Route::put("/v1/backup/create", [SettingsAPIController::class, 'createBackup']);
|
Route::put("/v1/backup/create", [SettingsAPIController::class, 'createBackup']);
|
||||||
Route::get("/v1/backup/download", [SettingsAPIController::class, 'downloadBackup']);
|
Route::get("/v1/backup/content-download", [SettingsAPIController::class, 'downloadBackup']);
|
||||||
|
Route::get("/v1/backup/files-download", [SettingsAPIController::class, 'downloadBackup']);
|
||||||
//init
|
//init
|
||||||
Route::post("/v1/init", [InitAPIController::class, 'setupFresh']);
|
Route::post("/v1/init", [InitAPIController::class, 'setupFresh']);
|
||||||
Route::post("/v1/restore", [InitAPIController::class, 'setupRestore']);
|
Route::post("/v1/restore", [InitAPIController::class, 'setupRestore']);
|
||||||
|
|
Loading…
Reference in a new issue