Added methods to delete speedtests

This commit is contained in:
Henry Whitaker
2020-07-22 01:40:19 +01:00
parent 78260c014a
commit a952c09e70
13 changed files with 246 additions and 14 deletions

View File

@@ -5,6 +5,7 @@ namespace App\Helpers;
use App\Speedtest; use App\Speedtest;
use Carbon\Carbon; use Carbon\Carbon;
use Exception; use Exception;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -253,4 +254,55 @@ class SpeedtestHelper {
return $rate; return $rate;
} }
/**
* Create a backup of the SQLite database
*
* @return boolean
*/
public static function dbBackup()
{
if(env('DB_CONNECTION') === 'sqlite') {
if(env('DB_DATABASE') !== null) {
$current = env('DB_DATABASE');
if(File::copy($current, $current . '.bak')) {
return true;
}
}
return false;
}
return null;
}
/**
* Delete all speedtests from the database
*
* @return boolean|string
*/
public static function deleteAll()
{
Cache::flush();
if(SpeedtestHelper::dbBackup() !== false) {
if(sizeof(Speedtest::whereNotNull('id')->get()) > 0) {
if(Speedtest::whereNotNull('id')->delete()) {
return [
'success' => true,
];
}
}
return [
'success' => true,
];
}
return [
'success' => false,
'msg' => 'There was an error backing up the database. No speedtests have been deleted.'
];
}
} }

View File

@@ -147,4 +147,45 @@ class SpeedtestController extends Controller
], 500); ], 500);
} }
} }
/**
* Delete all speedtests from db
*
* @return Response
*/
public function deleteAll()
{
$ret = SpeedtestHelper::deleteAll();
if($ret['success']) {
return response()->json([
'method' => 'delete all speedtests from the database',
'success' => true
], 200);
}
return response()->json([
'method' => 'delete all speedtests from the database',
'success' => false,
'error' => $ret['msg'],
], 500);
}
/**
* Delete a specific speedtest from the database
*
* @param Speedtest $speedtest
* @return boolean
*/
public function delete(Speedtest $speedtest)
{
$speedtest->delete();
Cache::flush();
return response()->json([
'method' => 'delete a speedtest from the database',
'success' => true,
], 200);
}
} }

1
database/.gitignore vendored
View File

@@ -1,2 +1,3 @@
*.sqlite *.sqlite
*.sqlite-journal *.sqlite-journal
*.bak

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,8 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Modal } from 'react-bootstrap'; import { Modal, Button } from 'react-bootstrap';
import Axios from 'axios';
import { toast } from 'react-toastify';
export default class TableRow extends Component { export default class TableRow extends Component {
constructor(props) { constructor(props) {
@@ -25,6 +27,25 @@ export default class TableRow extends Component {
} }
} }
delete = (id) => {
var url = 'api/speedtest/delete/' + id;
Axios.delete(url)
.then((resp) => {
console.log(resp);
toast.success('Speedtest deleted');
})
.catch((err) => {
if(err.response.status == 404) {
toast.warning('Speedtest not found');
} else {
toast.error('Something went wrong');
}
})
this.toggleShow();
}
render() { render() {
var e = this.state.data; var e = this.state.data;
var show = this.state.show; var show = this.state.show;
@@ -52,6 +73,7 @@ export default class TableRow extends Component {
{e.scheduled != undefined && {e.scheduled != undefined &&
<p>Type: {e.scheduled == true ? 'scheduled' : 'manual'}</p> <p>Type: {e.scheduled == true ? 'scheduled' : 'manual'}</p>
} }
<Button variant="danger" onClick={() => { this.delete(e.id) }}>Delete</Button>
</Modal.Body> </Modal.Body>
</Modal> </Modal>
</td> </td>

View File

@@ -5,7 +5,7 @@ import LatestResults from '../Graphics/LatestResults';
import Footer from './Footer'; import Footer from './Footer';
import DataRow from '../Data/DataRow'; import DataRow from '../Data/DataRow';
import TestsTable from '../Graphics/TestsTable'; import TestsTable from '../Graphics/TestsTable';
import Settings from './Settings'; import Settings from '../Settings/Settings';
export default class HomePage extends Component { export default class HomePage extends Component {

View File

@@ -0,0 +1,66 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Modal, Button } from 'react-bootstrap';
import SettingsModalCard from './SettingsModalCard';
import Axios from 'axios';
import { toast } from 'react-toastify';
export default class ResetSettings extends Component {
constructor(props) {
super(props)
this.state = {
show: false,
}
}
toggleShow = () => {
if(this.state.show) {
this.setState({ show: false });
} else {
this.setState({ show:true });
}
}
deleteAll = () => {
var url = 'api/speedtest/delete/all';
Axios.delete(url)
.then((resp) => {
toast.success('All speedtests have been deleted.');
this.toggleShow();
})
.catch((err) => {
if(err.response.data.error == undefined) {
toast.error('Something went wrong.');
}
toast.error(err.response.data.error);
})
}
render() {
var show = this.state.show;
const title = 'Reset Speedtests';
return (
<>
<SettingsModalCard title={title} description="Bulk delete speedtests from the database." toggleShow={this.toggleShow} />
<Modal show={show} onHide={this.toggleShow}>
<Modal.Header>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Clear all speedtests</h4>
<p className="text-muted">If using SQLite, a backup of the database will be stored in the location of the current database.</p>
<Button onClick={this.deleteAll} variant="danger">Delete all</Button>
</Modal.Body>
</Modal>
</>
);
}
}
if (document.getElementById('ResetSettings')) {
ReactDOM.render(<ResetSettings />, document.getElementById('ResetSettings'));
}

View File

@@ -3,6 +3,7 @@ import ReactDOM from 'react-dom';
import { Card, Form, Button, Modal, Row, Col } from 'react-bootstrap'; import { Card, Form, Button, Modal, Row, Col } from 'react-bootstrap';
import Axios from 'axios'; import Axios from 'axios';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import SettingsModalCard from '../Settings/SettingsModalCard';
export default class SettingWithModal extends Component { export default class SettingWithModal extends Component {
constructor(props) { constructor(props) {
@@ -98,15 +99,7 @@ export default class SettingWithModal extends Component {
return ( return (
<> <>
<Card className="m-2 setting-card"> <SettingsModalCard title={title} description={description} toggleShow={this.toggleShow} />
<Card.Body className="d-flex align-items-center">
<div>
<h4>{title}</h4>
<p>{description}</p>
<Button variant="primary" onClick={this.toggleShow}>Edit</Button>
</div>
</Card.Body>
</Card>
<Modal show={show} onHide={this.toggleShow}> <Modal show={show} onHide={this.toggleShow}>
<Modal.Header> <Modal.Header>
<Modal.Title>{title}</Modal.Title> <Modal.Title>{title}</Modal.Title>
@@ -130,7 +123,15 @@ export default class SettingWithModal extends Component {
readonly = true; readonly = true;
} }
if(e.type == 'checkbox') { if(e.type == 'info') {
return (
<Row key={e.obj.id} className="d-flex align-items-center">
<Col md={md} sm={sm}>
<p>{e.obj.content}</p>
</Col>
</Row>
)
} else if(e.type == 'checkbox') {
return ( return (
<Row key={e.obj.id} className="d-flex align-items-center"> <Row key={e.obj.id} className="d-flex align-items-center">
<Col md={md} sm={sm}> <Col md={md} sm={sm}>

View File

@@ -5,6 +5,7 @@ import Loader from '../Loader';
import Axios from 'axios'; import Axios from 'axios';
import Setting from './Setting'; import Setting from './Setting';
import SettingWithModal from './SettingWithModal'; import SettingWithModal from './SettingWithModal';
import ResetSettings from './ResetSettings';
export default class Settings extends Component { export default class Settings extends Component {
constructor(props) { constructor(props) {
@@ -157,6 +158,9 @@ export default class Settings extends Component {
} }
]} /> ]} />
</Col> </Col>
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
<ResetSettings />
</Col>
</Row> </Row>
) )
} }

View File

@@ -0,0 +1,37 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Card, Button } from 'react-bootstrap';
export default class SettingsModalCard extends Component {
constructor(props) {
super(props)
this.state = {
title: this.props.title,
description: this.props.description,
toggleShow: this.props.toggleShow,
}
}
render() {
var title = this.state.title;
var description = this.state.description;
var toggleShow = this.state.toggleShow;
return (
<Card className="m-2 setting-card">
<Card.Body className="d-flex align-items-center">
<div>
<h4>{title}</h4>
<p>{description}</p>
<Button variant="primary" onClick={toggleShow}>Edit</Button>
</div>
</Card.Body>
</Card>
);
}
}
if (document.getElementById('SettingModalCard')) {
ReactDOM.render(<SettingsModalCard />, document.getElementById('SettingModalCard'));
}

View File

@@ -1,5 +1,6 @@
<?php <?php
use App\Helpers\SpeedtestHelper;
use App\Http\Controllers\SpeedtestController; use App\Http\Controllers\SpeedtestController;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@@ -29,6 +30,13 @@ Route::group([
->name('speedtest.fail'); ->name('speedtest.fail');
Route::get('run', 'SpeedtestController@run') Route::get('run', 'SpeedtestController@run')
->name('speedtest.run'); ->name('speedtest.run');
Route::group([
'prefix' => 'delete'
], function () {
Route::delete('all', 'SpeedtestController@deleteAll');
Route::delete('{speedtest}', 'SpeedtestController@delete');
});
}); });
Route::group([ Route::group([

View File

@@ -30,6 +30,6 @@ Route::get(SettingsHelper::getBase() . 'files/{path?}', function($file) {
->name('files'); ->name('files');
Route::get('/{path?}', function() { Route::get('/{path?}', function() {
return view('app', [ 'title' => 'Speedtest Checker' ]); return view('app', [ 'title' => 'Speedtest Tracker' ]);
})->where('path', '^((?!\/api\/).)*$') })->where('path', '^((?!\/api\/).)*$')
->name('react'); ->name('react');