App   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Importance

Changes 5
Bugs 2 Features 0
Metric Value
wmc 23
eloc 108
c 5
b 2
f 0
dl 0
loc 199
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A updateBackupStatuses() 0 33 4
A respondWithBackupStream() 0 22 3
A deleteFile() 0 27 4
A render() 0 3 1
A showDeleteModal() 0 5 1
A validateActiveDisk() 0 17 2
A getFiles() 0 23 3
A validateFilePath() 0 17 2
A createBackup() 0 4 1
A downloadFile() 0 16 2
1
<?php
2
3
namespace PavelMironchik\LaravelBackupPanel\Http\Livewire;
4
5
use Illuminate\Http\Response;
6
use Illuminate\Support\Facades\Cache;
7
use Illuminate\Support\Facades\Validator;
8
use Illuminate\Validation\ValidationException;
9
use Livewire\Component;
10
use PavelMironchik\LaravelBackupPanel\Jobs\CreateBackupJob;
11
use PavelMironchik\LaravelBackupPanel\Rules\BackupDisk;
12
use PavelMironchik\LaravelBackupPanel\Rules\PathToZip;
13
use Spatie\Backup\BackupDestination\Backup;
14
use Spatie\Backup\BackupDestination\BackupDestination;
15
use Spatie\Backup\Helpers\Format;
16
use Spatie\Backup\Tasks\Monitor\BackupDestinationStatus;
17
use Spatie\Backup\Tasks\Monitor\BackupDestinationStatusFactory;
18
use Symfony\Component\HttpFoundation\StreamedResponse;
19
20
class App extends Component
21
{
22
    public $backupStatuses = [];
23
24
    public $activeDisk = null;
25
26
    public $disks = [];
27
28
    public $files = [];
29
30
    public $deletingFile = null;
31
32
    public function updateBackupStatuses()
33
    {
34
        $this->backupStatuses = Cache::remember('backup-statuses', now()->addSeconds(4), function () {
35
            return BackupDestinationStatusFactory::createForMonitorConfig(config('backup.monitor_backups'))
36
                ->map(function (BackupDestinationStatus $backupDestinationStatus) {
37
                    return [
38
                        'name' => $backupDestinationStatus->backupDestination()->backupName(),
39
                        'disk' => $backupDestinationStatus->backupDestination()->diskName(),
40
                        'reachable' => $backupDestinationStatus->backupDestination()->isReachable(),
41
                        'healthy' => $backupDestinationStatus->isHealthy(),
42
                        'amount' => $backupDestinationStatus->backupDestination()->backups()->count(),
43
                        'newest' => $backupDestinationStatus->backupDestination()->newestBackup()
44
                            ? $backupDestinationStatus->backupDestination()->newestBackup()->date()->diffForHumans()
45
                            : 'No backups present',
46
                        'usedStorage' => Format::humanReadableSize($backupDestinationStatus->backupDestination()->usedStorage()),
47
                    ];
48
                })
49
                ->values()
50
                ->toArray();
51
        });
52
53
        if (! $this->activeDisk and count($this->backupStatuses)) {
54
            $this->activeDisk = $this->backupStatuses[0]['disk'];
55
        }
56
57
        $this->disks = collect($this->backupStatuses)
58
            ->map(function ($backupStatus) {
59
                return $backupStatus['disk'];
60
            })
61
            ->values()
62
            ->all();
63
64
        $this->emitSelf('backupStatusesUpdated');
65
    }
66
67
    public function getFiles(string $disk = '')
68
    {
69
        if ($disk) {
70
            $this->activeDisk = $disk;
71
        }
72
73
        $this->validateActiveDisk();
74
75
        $backupDestination = BackupDestination::create($this->activeDisk, config('backup.backup.name'));
76
77
        $this->files = Cache::remember("backups-{$this->activeDisk}", now()->addSeconds(4), function () use ($backupDestination) {
78
            return $backupDestination
79
                ->backups()
80
                ->map(function (Backup $backup) {
81
                    $size = method_exists($backup, 'sizeInBytes') ? $backup->sizeInBytes() : $backup->size();
82
83
                    return [
84
                        'path' => $backup->path(),
85
                        'date' => $backup->date()->format('Y-m-d H:i:s'),
86
                        'size' => Format::humanReadableSize($size),
87
                    ];
88
                })
89
                ->toArray();
90
        });
91
    }
92
93
    public function showDeleteModal($fileIndex)
94
    {
95
        $this->deletingFile = $this->files[$fileIndex];
96
97
        $this->emitSelf('showDeleteModal');
98
    }
99
100
    public function deleteFile()
101
    {
102
        $deletingFile = $this->deletingFile;
103
        $this->deletingFile = null;
104
105
        $this->emitSelf('hideDeleteModal');
106
107
        $this->validateActiveDisk();
108
        $this->validateFilePath($deletingFile ? $deletingFile['path'] : '');
109
110
        $backupDestination = BackupDestination::create($this->activeDisk, config('backup.backup.name'));
111
112
        $backupDestination
113
            ->backups()
114
            ->first(function (Backup $backup) use ($deletingFile) {
115
                return $backup->path() === $deletingFile['path'];
116
            })
117
            ->delete();
118
119
        $this->files = collect($this->files)
120
            ->reject(function ($file) use ($deletingFile) {
121
                return $file['path'] === $deletingFile['path']
122
                    && $file['date'] === $deletingFile['date']
123
                    && $file['size'] === $deletingFile['size'];
124
            })
125
            ->values()
126
            ->all();
127
    }
128
129
    public function downloadFile(string $filePath)
130
    {
131
        $this->validateActiveDisk();
132
        $this->validateFilePath($filePath);
133
134
        $backupDestination = BackupDestination::create($this->activeDisk, config('backup.backup.name'));
135
136
        $backup = $backupDestination->backups()->first(function (Backup $backup) use ($filePath) {
137
            return $backup->path() === $filePath;
138
        });
139
140
        if (! $backup) {
141
            return response('Backup not found', Response::HTTP_UNPROCESSABLE_ENTITY);
142
        }
143
144
        return $this->respondWithBackupStream($backup);
145
    }
146
147
    public function respondWithBackupStream(Backup $backup): StreamedResponse
148
    {
149
        $fileName = pathinfo($backup->path(), PATHINFO_BASENAME);
150
        $size = method_exists($backup, 'sizeInBytes') ? $backup->sizeInBytes() : $backup->size();
151
152
        $downloadHeaders = [
153
            'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
154
            'Content-Type' => 'application/zip',
155
            'Content-Length' => $size,
156
            'Content-Disposition' => 'attachment; filename="'.$fileName.'"',
157
            'Pragma' => 'public',
158
        ];
159
160
        return response()->stream(function () use ($backup) {
161
            $stream = $backup->stream();
162
163
            fpassthru($stream);
164
165
            if (is_resource($stream)) {
166
                fclose($stream);
167
            }
168
        }, 200, $downloadHeaders);
169
    }
170
171
    public function createBackup(string $option = '')
172
    {
173
        dispatch(new CreateBackupJob($option))
174
            ->onQueue(config('laravel_backup_panel.queue'));
175
    }
176
177
    public function render()
178
    {
179
        return view('laravel_backup_panel::livewire.app');
180
    }
181
182
    protected function validateActiveDisk()
183
    {
184
        try {
185
            Validator::make(
186
                ['activeDisk' => $this->activeDisk],
187
                [
188
                    'activeDisk' => ['required', new BackupDisk()],
189
                ],
190
                [
191
                    'activeDisk.required' => 'Select a disk',
192
                ]
193
            )->validate();
194
        } catch (ValidationException $e) {
195
            $message = $e->validator->errors()->get('activeDisk')[0];
196
            $this->emitSelf('showErrorToast', $message);
197
198
            throw $e;
199
        }
200
    }
201
202
    protected function validateFilePath(string $filePath)
203
    {
204
        try {
205
            Validator::make(
206
                ['file' => $filePath],
207
                [
208
                    'file' => ['required', new PathToZip()],
209
                ],
210
                [
211
                    'file.required' => 'Select a file',
212
                ]
213
            )->validate();
214
        } catch (ValidationException $e) {
215
            $message = $e->validator->errors()->get('file')[0];
216
            $this->emitSelf('showErrorToast', $message);
217
218
            throw $e;
219
        }
220
    }
221
}
222