Skip to content

Instantly share code, notes, and snippets.

@kikoseijo
Last active October 15, 2023 18:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kikoseijo/d4ec87a121cba7bcb6231bfa046be291 to your computer and use it in GitHub Desktop.
Save kikoseijo/d4ec87a121cba7bcb6231bfa046be291 to your computer and use it in GitHub Desktop.
Laravel Spatie backup with controller routes and views
<?php
namespace App\Http\Controllers;
use Alert;
use Artisan;
use Carbon\Carbon;
use Log;
use Spatie\Backup\Helpers\Format;
use Storage;
class BackupController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
$disk = Storage::disk(config('backup.backup.destination.disks')[0]);
$files = $disk->files(config('backup.backup.name'));
$backups = [];
// make an array of backup files, with their filesize and creation date
foreach ($files as $k => $f) {
// only take the zip files into account
if (substr($f, -4) == '.zip' && $disk->exists($f)) {
$backups[] = [
'file_path' => $f,
'file_name' => str_replace(config('backup.backup.name') . '/', '', $f),
'file_size' => Format::humanReadableSize($disk->size($f)),
'last_modified' => Carbon::createFromTimestamp($disk->lastModified($f)),
];
}
}
// reverse the backups, so the newest one would be on top
$backups = array_reverse($backups);
return view("admin.backups")->with(compact('backups'));
}
public function create()
{
try {
// start the backup process
Artisan::call('backup:run', ['--only-db' => 'true']);
$output = Artisan::output();
// log the results
Log::info("Backpack\BackupManager -- new backup started from admin interface \r\n" . $output);
// return the results as a response to the ajax call
Alert::success('New backup created');
return redirect()->back();
} catch (Exception $e) {
Flash::error($e->getMessage());
return redirect()->back();
}
}
/**
* Downloads a backup zip file.
*
* TODO: make it work no matter the flysystem driver (S3 Bucket, etc).
*/
public function download($file_name)
{
$file = config('backup.backup.name') . '/' . $file_name;
$disk = Storage::disk(config('backup.backup.destination.disks')[0]);
if ($disk->exists($file)) {
$fs = Storage::disk(config('backup.backup.destination.disks')[0])->getDriver();
$stream = $fs->readStream($file);
return \Response::stream(function () use ($stream) {
fpassthru($stream);
}, 200, [
"Content-Type" => $fs->getMimetype($file),
"Content-Length" => $fs->getSize($file),
"Content-disposition" => "attachment; filename=\"" . basename($file) . "\"",
]);
} else {
abort(404, "The backup file doesn't exist.");
}
}
/**
* Deletes a backup file.
*/
public function delete($file_name)
{
$disk = Storage::disk(config('backup.backup.destination.disks')[0]);
if ($disk->exists(config('backup.backup.name') . '/' . $file_name)) {
$disk->delete(config('backup.backup.name') . '/' . $file_name);
return redirect()->back();
} else {
abort(404, "The backup file doesn't exist.");
}
}
}
@if (count($backups))
<table class="table table-striped table-bordered">
<thead class="thead-dark">
<tr>
<th>File</th>
<th>Size</th>
<th>Date</th>
<th>Age</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach($backups as $backup)
<tr>
<td>{{ $backup['file_name'] }}</td>
<td>{{ $backup['file_size'] }}</td>
<td>
{{ date('d/M/Y, g:ia', strtotime($backup['last_modified'])) }}
</td>
<td>
{{ diff_date_for_humans($backup['last_modified']) }}
</td>
<td class="text-right">
<a class="btn btn-primary" href="{{ url('backup/download/'.$backup['file_name']) }}">
<i class="fas fa-cloud-download"></i> Download</a>
<a class="btn btn-xs btn-danger" data-button-type="delete" href="{{ url('backup/delete/'.$backup['file_name']) }}">
<i class="fal fa-trash"></i>
Delete
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
@else
<div class="text-center py-5">
<h1 class="text-muted">No existen backups</h1>
</div>
@endif
@extends('layouts.crud')
@section('content')
<div class="container -body-block pb-5">
@card(['title' => 'Backups de la base de datos'])
@component('ui.menu-nav')
<li class="nav-item active mr-3">
<a href="{{ url('backup/create') }}" class="nav-link text-primary" title="Crear nuevo backup">
<i class="far fa-plus" aria-hidden="true"></i> Crear nuevo backup
</a>
</li>
@endcomponent
<div class="py-4"></div>
@include('admin.backups-table')
<div class="py-3"></div>
@endcard
</div>
@endsection
<div class="row">
<div class="col">
<div class="card">
@isset($title)
<div class="card-header">{{$title}}</div>
@endisset
<div class="card-body">
{{ $slot }}
</div>
</div>
</div>
</div>
<!DOCTYPE html>
<html lang="{{ config('app.locale') }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Sunnyface.com') }}</title>
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<script>
window.AppRootData = {!! json_encode([
'csrfToken' => csrf_token(),
'state' => ['user' => Auth::user()]
]) !!};
</script>
</head>
<body>
<div id="app" class="boxx-wrapper">
@include('parts.header.admin')
<div class="album py-5 bg-light">
@include('ui.errors')
@yield('content')
</div>
@include('parts.footer')
@stack('modals')
</div>
<!-- Scripts -->
@if (app()->isLocal())
<script src="{{ mix('js/app.js') }}"></script>
@else
<script src="{{ mix('js/manifest.js') }}"></script>
<script src="{{ mix('js/vendor.js') }}"></script>
<script src="{{ mix('js/app.js') }}"></script>
@endif
@stack('scripts')
</body>
</html>
<?php
use Carbon\Carbon;
function diff_date_for_humans(Carbon $date) : string
{
return (new Jenssegers\Date\Date($date->timestamp))->ago();
}
function diff_string_for_humans($stringDate) : string
{
$date = Jenssegers\Date\Date::createFromFormat('Y-m-d H:i:s', $stringDate);
return (new Jenssegers\Date\Date($date))->ago();
}
function scannerTableLabel($stringDate) : string
{
$now = Jenssegers\Date\Date::now();
$date = Jenssegers\Date\Date::createFromFormat('Y-m-d H:i:s', $stringDate);
$printDate = (new Jenssegers\Date\Date($date))->ago();
$color = $now > $date ? 'info' : 'danger';
$res = '<span class="badge badge-'.$color.'" style="color:white;">SCANNER: ';
$res .= $printDate ;
$res .= '</span>';
return $res;
}
<nav class="navbar navbar-expand-md navbar-light bg-light">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#table-actions-nav-menu" aria-controls="table-actions-nav-menu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="table-actions-nav-menu">
<ul class="navbar-nav mr-auto mt-2 mt-lg-0">
{{ $slot }}
</ul>
</div>
</nav>
Route::get('backup', 'BackupController@index');
Route::get('backup/create', 'BackupController@create');
Route::get('backup/download/{file_name}', 'BackupController@download');
Route::get('backup/delete/{file_name}', 'BackupController@delete');
@kikoseijo
Copy link
Author

No está al completo, pero le falta lo mínimo.

Credits

Special thanks to supporters and clients that provide me with enough time to work on contributing to develop this packages for the WWW.

DevOps Web development
AppDev Mobile aplications
SocialApp Residents mobile application
KikoSeijo.com Freelance senior programmer


Created by Kiko Seijo

@qalildarwish
Copy link

qalildarwish commented Nov 13, 2022

Hello Sir,
Thanks for your instruction and efforts you solved big problem of me, but there is some error for me this code is working for in local server but in Cpanel it give the following error
Connection could not be established with host "mailhog:1025": stream_socket_client(): php_network_getaddresses: getaddrinfo for mailhog failed: Name or service not known

@kikoseijo
Copy link
Author

Hi, Because its based on a plugin for backups from spatie, its trying to send you an email and this is the error you are getting i suppose.
use Spatie\Backup\Helpers\Format;

Check spatie plugin for further customization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment