diff --git a/app/Helpers/BackupHelper.php b/app/Helpers/BackupHelper.php index ecc4a2a4..b62beaee 100644 --- a/app/Helpers/BackupHelper.php +++ b/app/Helpers/BackupHelper.php @@ -5,6 +5,7 @@ namespace App\Helpers; use App\Speedtest; use DateTime; use Exception; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; class BackupHelper { @@ -42,19 +43,49 @@ class BackupHelper { return $name; } - public static function restore($array) + public static function restore($array, $format) { - foreach($array as $test) { - try { - $st = Speedtest::create([ - 'ping' => $test['ping'], - 'download' => $test['download'], - 'upload' => $test['upload'], - 'created_at' => $test['created_at'], - ]); - } catch(Exception $e) { - continue; + if($format == 'json') { + foreach($array as $test) { + try { + $st = Speedtest::create([ + 'ping' => $test['ping'], + 'download' => $test['download'], + 'upload' => $test['upload'], + 'created_at' => $test['created_at'], + ]); + } catch(Exception $e) { + continue; + } } + return true; + } else if($format == 'csv') { + $csv = explode(PHP_EOL, $array); + $headers = 'id,ping,download,upload,created_at,updated_at'; + if($csv[0] != $headers) { + Log::error('Incorrect CSV format'); + return false; + } + + unset($csv[0]); + $csv = array_values($csv); + + for($i = 0; $i < sizeof($csv); $i++) { + $e = explode(',', $csv[$i]); + try { + $st = Speedtest::create([ + 'ping' => $e[1], + 'download' => $e[2], + 'upload' => $e[3], + 'created_at' => substr($e[4], 1, -1), + ]); + } catch(Exception $e) { + Log::error($e); + continue; + } + } + + return true; } } } diff --git a/app/Http/Controllers/BackupController.php b/app/Http/Controllers/BackupController.php index a0fc9823..634a6c80 100644 --- a/app/Http/Controllers/BackupController.php +++ b/app/Http/Controllers/BackupController.php @@ -28,7 +28,8 @@ class BackupController extends Controller public function restore(Request $request) { $rule = [ - 'data' => [ 'required', 'array' ], + 'data' => [ 'required' ], + 'format' => [ 'required', 'in:json,csv' ] ]; $validator = Validator::make($request->all(), $rule); @@ -39,10 +40,14 @@ class BackupController extends Controller ], 422); } - BackupHelper::restore($request->data); - - return response()->json([ - 'method' => 'restore data from backup', - ], 200); + if(BackupHelper::restore($request->data, $request->format) != false) { + return response()->json([ + 'method' => 'restore data from backup', + ], 200); + } else { + return response()->json([ + 'method' => 'incorrect backup format', + ], 422); + } } } diff --git a/resources/js/components/Data/Restore.js b/resources/js/components/Data/Restore.js index 9df8462b..d9104006 100644 --- a/resources/js/components/Data/Restore.js +++ b/resources/js/components/Data/Restore.js @@ -1,8 +1,9 @@ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; -import { Button, Modal, Form, Tooltip, OverlayTrigger } from 'react-bootstrap'; +import { Button, Modal, Form, Tooltip, OverlayTrigger, Dropdown, DropdownButton } from 'react-bootstrap'; import { toast } from 'react-toastify'; import Axios from 'axios'; +import CSVFileValidator from 'csv-file-validator'; export default class Restore extends Component { constructor(props) { @@ -12,13 +13,15 @@ export default class Restore extends Component { show: false, data: null, uploadReady: false, - filename: 'Upload your backup JSON' + filename: 'Upload your backup', + format: 'json' }; } - showModal = () => { + showModal = (format) => { this.setState({ - show: true + show: true, + format: format }); } @@ -28,22 +31,84 @@ export default class Restore extends Component { }); } - readFile = (e) => { + readFile = (e, format) => { var file = e.target.files[0]; var reader = new FileReader(); reader.readAsText(file, 'UTF-8'); reader.onload = function(evt) { - try { - var data = evt.target.result.trim(); - var data = JSON.parse(data); - this.setState({ - data: data, - uploadReady: true, - filename: file.name - }); - } catch(e) { - console.log(e); - toast.error('Your upload file is not valid JSON'); + var data = evt.target.result.trim(); + if(format == 'csv') { + var csv = data.substr(45); + var config = { + headers: [ + { + name: "id", + inputName: 'id', + required: false, + }, + { + name: "ping", + inputName: 'ping', + required: true, + requiredError: function (headerName, rowNumber, columnNumber) { + return `${headerName} is required in the ${rowNumber} row / ${columnNumber} column` + } + }, + { + name: "upload", + inputName: 'upload', + required: true, + requiredError: function (headerName, rowNumber, columnNumber) { + return `${headerName} is required in the ${rowNumber} row / ${columnNumber} column` + } + }, + { + name: "download", + inputName: 'download', + required: true, + requiredError: function (headerName, rowNumber, columnNumber) { + return `${headerName} is required in the ${rowNumber} row / ${columnNumber} column` + } + }, + { + name: "created_at", + inputName: 'created_at', + required: false, + }, + { + name: "updated_at", + inputName: 'updated_at', + required: false, + } + ] + }; + CSVFileValidator(csv, config) + .then((e) => { + if(e.inValidMessages.length > 0) { + toast.error('Your upload file is not valid ' + format.toUpperCase()); + } else { + this.setState({ + data: data, + uploadReady: true, + filename: file.name + }); + } + }) + .catch((e) => { + toast.error('Your upload file is not valid ' + format.toUpperCase()); + }) + } else { + try { + var data = JSON.parse(data); + this.setState({ + data: data, + uploadReady: true, + filename: file.name + }); + } catch(e) { + console.log(e); + toast.error('Your upload file is not valid ' + format.toUpperCase()); + } } }.bind(this) reader.onerror = function (evt) { @@ -52,7 +117,7 @@ export default class Restore extends Component { } uploadFile = () => { - var data = { data: this.state.data }; + var data = { data: this.state.data, format: this.state.format }; var url = 'api/restore'; Axios.post(url, data) @@ -62,7 +127,7 @@ export default class Restore extends Component { show: false, data: null, uploadReady: false, - filename: 'Upload your backup JSON' + filename: 'Upload your backup' }); }) .catch((err) => { @@ -77,21 +142,24 @@ export default class Restore extends Component { return ( <> - Restore + + { this.showModal('json') }}>JSON + { this.showModal('csv') }}>CSV + Restore from a backup - Upload your JSON backup file here: + Upload your {this.state.format.toUpperCase()} backup file here: - + { this.readFile(e, this.state.format) }} /> {filename}
Upload your JSON backup file here:
Upload your {this.state.format.toUpperCase()} backup file here: