mirror of
https://github.com/henrywhitaker3/Speedtest-Tracker.git
synced 2025-12-21 13:23:04 +01:00
Added failure graph and graph settings panel
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Speedtest Tracker
|
# Speedtest Tracker
|
||||||
|
|
||||||
[](https://hub.docker.com/r/henrywhitaker3/speedtest-tracker) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits) [](https://github.com/henrywhitaker3/Speedtest-Tracker/issues) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits)  [](https://github.com/henrywhitaker3/Speedtest-Tracker/blob/master/LICENSE)
|
[](https://hub.docker.com/r/henrywhitaker3/speedtest-tracker) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits) [](https://github.com/henrywhitaker3/Speedtest-Tracker/issues) [](https://github.com/henrywhitaker3/Speedtest-Tracker/commits)  [](https://github.com/henrywhitaker3/Speedtest-Tracker/blob/master/LICENSE)
|
||||||
|
|
||||||
This program runs a speedtest check every hour and graphs the results. The back-end is written in [Laravel](https://laravel.com/) and the front-end uses [React](https://reactjs.org/). It uses the [Ookla's speedtest cli](https://www.speedtest.net/apps/cli) package to get the data and uses [Chart.js](https://www.chartjs.org/) to plot the results.
|
This program runs a speedtest check every hour and graphs the results. The back-end is written in [Laravel](https://laravel.com/) and the front-end uses [React](https://reactjs.org/). It uses the [Ookla's speedtest cli](https://www.speedtest.net/apps/cli) package to get the data and uses [Chart.js](https://www.chartjs.org/) to plot the results.
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
use App\Setting;
|
use App\Setting;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
class SettingsHelper {
|
class SettingsHelper {
|
||||||
|
|
||||||
@@ -38,6 +39,9 @@ class SettingsHelper {
|
|||||||
$setting = SettingsHelper::get($name);
|
$setting = SettingsHelper::get($name);
|
||||||
|
|
||||||
if($setting !== false) {
|
if($setting !== false) {
|
||||||
|
if($value == false) {
|
||||||
|
$value = "0";
|
||||||
|
}
|
||||||
$setting->value = $value;
|
$setting->value = $value;
|
||||||
$setting->save();
|
$setting->save();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
@@ -63,6 +64,8 @@ class SpeedtestHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Cache::flush();
|
||||||
|
|
||||||
return $test;
|
return $test;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,4 +210,47 @@ class SpeedtestHelper {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a percentage rate of failure by days
|
||||||
|
*
|
||||||
|
* @param integer $days number of days to get rate for
|
||||||
|
* @return integer percentage fail rate
|
||||||
|
*/
|
||||||
|
public static function failureRate(int $days)
|
||||||
|
{
|
||||||
|
$ttl = Carbon::now()->addDays(1);
|
||||||
|
$rate = Cache::remember('failure-rate-' . $days, $ttl, function () use ($days) {
|
||||||
|
$range = [
|
||||||
|
Carbon::today()
|
||||||
|
];
|
||||||
|
for($i = 0; $i < $days; $i++) {
|
||||||
|
$prev = end($range);
|
||||||
|
$new = $prev->copy()->subDays(1);
|
||||||
|
array_push($range, $new);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rate = [];
|
||||||
|
|
||||||
|
foreach($range as $day) {
|
||||||
|
$success = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', false)->get()[0]['rate'];
|
||||||
|
$fail = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', true)->get()[0]['rate'];
|
||||||
|
|
||||||
|
if(( $success + $fail ) == 0) {
|
||||||
|
$percentage = 0;
|
||||||
|
} else {
|
||||||
|
$percentage = round(( $fail / ( $success + $fail ) * 100 ), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push($rate, [
|
||||||
|
'date' => $day->toDateString(),
|
||||||
|
'rate' => $percentage
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_reverse($rate);
|
||||||
|
});
|
||||||
|
|
||||||
|
return $rate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,7 +120,13 @@ class SettingsController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
$config = [
|
$config = [
|
||||||
'base' => SettingsHelper::getBase()
|
'base' => SettingsHelper::getBase(),
|
||||||
|
'download_upload_graph_enabled' => SettingsHelper::get('download_upload_graph_enabled'),
|
||||||
|
'download_upload_graph_width' => SettingsHelper::get('download_upload_graph_width'),
|
||||||
|
'ping_graph_enabled' => SettingsHelper::get('ping_graph_enabled'),
|
||||||
|
'ping_graph_width' => SettingsHelper::get('ping_graph_width'),
|
||||||
|
'failure_graph_enabled' => SettingsHelper::get('failure_graph_enabled'),
|
||||||
|
'failure_graph_width' => SettingsHelper::get('failure_graph_width'),
|
||||||
];
|
];
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use App\Speedtest;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
@@ -51,10 +52,43 @@ class SpeedtestController extends Controller
|
|||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = Speedtest::where('created_at', '>=', Carbon::now()->subDays($days))
|
$ttl = Carbon::now()->addDays(1);
|
||||||
->where('failed', false)
|
$data = Cache::remember('speedtest-days-' . $days, $ttl, function () use ($days) {
|
||||||
->orderBy('created_at', 'asc')
|
return Speedtest::where('created_at', '>=', Carbon::now()->subDays($days))
|
||||||
->get();
|
->where('failed', false)
|
||||||
|
->orderBy('created_at', 'asc')
|
||||||
|
->get();
|
||||||
|
});
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'get speedtests in last x days',
|
||||||
|
'days' => $days,
|
||||||
|
'data' => $data
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns speedtest failure rate going back 'x' days
|
||||||
|
*
|
||||||
|
* @param int $days
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function fail($days)
|
||||||
|
{
|
||||||
|
$rule = [
|
||||||
|
'days' => [ 'required', 'integer' ],
|
||||||
|
];
|
||||||
|
|
||||||
|
$validator = Validator::make([ 'days' => $days ], $rule);
|
||||||
|
|
||||||
|
if($validator->fails()) {
|
||||||
|
return response()->json([
|
||||||
|
'method' => 'get speedtests in last x days',
|
||||||
|
'error' => $validator->errors(),
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = SpeedtestHelper::failureRate($days);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get speedtests in last x days',
|
'method' => 'get speedtests in last x days',
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class Kernel extends HttpKernel
|
|||||||
],
|
],
|
||||||
|
|
||||||
'api' => [
|
'api' => [
|
||||||
'throttle:100,1',
|
'throttle:200,1',
|
||||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,18 @@
|
|||||||
{
|
{
|
||||||
|
"1.7.4": [
|
||||||
|
{
|
||||||
|
"description": "Stopped failed tests appearing in graphs",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Added failure rate graph",
|
||||||
|
"link": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Updated dependencies",
|
||||||
|
"link": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
"1.7.3": [
|
"1.7.3": [
|
||||||
{
|
{
|
||||||
"description": "Updated dependencies",
|
"description": "Updated dependencies",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ return [
|
|||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'version' => '1.7.3',
|
'version' => '1.7.4',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|||||||
70
database/migrations/2020_07_06_105930_add_graph_settings.php
Normal file
70
database/migrations/2020_07_06_105930_add_graph_settings.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddGraphSettings extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'download_upload_graph_enabled',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'Enable the download/upload graph'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'download_upload_graph_width',
|
||||||
|
'value' => 6,
|
||||||
|
'description' => 'Set the width of the download/upload graph'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'ping_graph_enabled',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'Enable the ping graph'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'ping_graph_width',
|
||||||
|
'value' => 6,
|
||||||
|
'description' => 'Set the width of the ping graph'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'failure_graph_enabled',
|
||||||
|
'value' => true,
|
||||||
|
'description' => 'Enable the failure rate graph'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'failure_graph_width',
|
||||||
|
'value' => 6,
|
||||||
|
'description' => 'Set the width of the failure rate graph'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'download_upload_graph_enabled',
|
||||||
|
'download_upload_graph_width',
|
||||||
|
'ping_graph_enabled',
|
||||||
|
'ping_graph_width',
|
||||||
|
'failure_graph_enabled',
|
||||||
|
'failure_graph_width'
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
10931
public/css/app.css
vendored
10931
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
4
public/css/main.css
vendored
4
public/css/main.css
vendored
@@ -55,3 +55,7 @@
|
|||||||
.setting-card {
|
.setting-card {
|
||||||
height: 270px;
|
height: 270px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-graph {
|
||||||
|
height: 480px;
|
||||||
|
}
|
||||||
|
|||||||
131746
public/js/app.js
vendored
131746
public/js/app.js
vendored
File diff suppressed because one or more lines are too long
170
resources/js/components/Graphics/HistoryGraph.js
vendored
170
resources/js/components/Graphics/HistoryGraph.js
vendored
@@ -16,8 +16,16 @@ export default class HistoryGraph extends Component {
|
|||||||
duOptions: {},
|
duOptions: {},
|
||||||
pingData: {},
|
pingData: {},
|
||||||
pingOptions: {},
|
pingOptions: {},
|
||||||
|
failData: {},
|
||||||
|
failOptions: {},
|
||||||
loading: true,
|
loading: true,
|
||||||
interval: null,
|
interval: null,
|
||||||
|
graph_ul_dl_enabled: true,
|
||||||
|
graph_ul_dl_width: 6,
|
||||||
|
graph_failure_enabled: true,
|
||||||
|
graph_failure_width: 6,
|
||||||
|
graph_ping_enabled: true,
|
||||||
|
graph_ping_width: 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +37,7 @@ export default class HistoryGraph extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getData = (days = this.state.days) => {
|
getDLULPing = (days) => {
|
||||||
var url = 'api/speedtest/time/' + days;
|
var url = 'api/speedtest/time/' + days;
|
||||||
|
|
||||||
Axios.get(url)
|
Axios.get(url)
|
||||||
@@ -52,6 +60,7 @@ export default class HistoryGraph extends Component {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
var duOptions = {
|
var duOptions = {
|
||||||
|
maintainAspectRatio: false,
|
||||||
tooltips: {
|
tooltips: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (item) => `${item.yLabel} Mbit/s`,
|
label: (item) => `${item.yLabel} Mbit/s`,
|
||||||
@@ -90,6 +99,7 @@ export default class HistoryGraph extends Component {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
var pingOptions = {
|
var pingOptions = {
|
||||||
|
maintainAspectRatio: false,
|
||||||
tooltips: {
|
tooltips: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: (item) => `${item.yLabel} ms`,
|
label: (item) => `${item.yLabel} ms`,
|
||||||
@@ -149,6 +159,97 @@ export default class HistoryGraph extends Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFailure = (days) => {
|
||||||
|
var url = 'api/speedtest/fail/' + days;
|
||||||
|
Axios.get(url)
|
||||||
|
.then((resp) => {
|
||||||
|
var failData = {
|
||||||
|
labels: [],
|
||||||
|
datasets:[
|
||||||
|
{
|
||||||
|
data: [],
|
||||||
|
label: 'Failure',
|
||||||
|
borderColor: "#E74C3C",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
var failOptions = {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
tooltips: {
|
||||||
|
callbacks: {
|
||||||
|
label: (item) => `${item.yLabel} %`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
text: 'Ping results for the last ' + days + ' days',
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
display: false,
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: 'DateTime'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
point:{
|
||||||
|
radius: 0,
|
||||||
|
hitRadius: 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.data.data.forEach(e => {
|
||||||
|
var date = new Date(e.date);
|
||||||
|
var fail = {
|
||||||
|
t: date,
|
||||||
|
y: e.rate
|
||||||
|
};
|
||||||
|
failData.datasets[0].data.push(fail);
|
||||||
|
failData.labels.push(date.getFullYear() + '/' + ('0' + (date.getMonth() + 1)).slice(-2) + '/' + ('0' + date.getDay()).slice(-2));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
failData: failData,
|
||||||
|
failOptions: failOptions
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getData = (days = this.state.days) => {
|
||||||
|
Axios.get('api/settings/config')
|
||||||
|
.then((resp) => {
|
||||||
|
var data = resp.data;
|
||||||
|
console.log(data)
|
||||||
|
this.setState({
|
||||||
|
graph_ul_dl_enabled: Boolean(Number(data.download_upload_graph_enabled.value)),
|
||||||
|
graph_ul_dl_width: data.download_upload_graph_width.value,
|
||||||
|
graph_ping_enabled: Boolean(Number(data.ping_graph_enabled.value)),
|
||||||
|
graph_ping_width: data.ping_graph_width.value,
|
||||||
|
graph_failure_enabled: Boolean(Number(data.failure_graph_enabled.value)),
|
||||||
|
graph_failure_width: data.failure_graph_width.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
if(this.state.graph_ul_dl_enabled || this.state.graph_ping_enabled) {
|
||||||
|
this.getDLULPing(days);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.state.graph_failure_enabled) {
|
||||||
|
this.getFailure(days);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('Couldn\'t get the site config');
|
||||||
|
console.log(err);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
updateDays = (e) => {
|
updateDays = (e) => {
|
||||||
var days = e.target.value;
|
var days = e.target.value;
|
||||||
if(days) {
|
if(days) {
|
||||||
@@ -169,8 +270,44 @@ export default class HistoryGraph extends Component {
|
|||||||
var duOptions = this.state.duOptions;
|
var duOptions = this.state.duOptions;
|
||||||
var pingData = this.state.pingData;
|
var pingData = this.state.pingData;
|
||||||
var pingOptions = this.state.pingOptions;
|
var pingOptions = this.state.pingOptions;
|
||||||
|
var failData = this.state.failData;
|
||||||
|
var failOptions = this.state.failOptions;
|
||||||
var days = this.state.days;
|
var days = this.state.days;
|
||||||
|
|
||||||
|
console.log(failData);
|
||||||
|
console.log(failOptions);
|
||||||
|
console.log(pingData);
|
||||||
|
console.log(pingOptions);
|
||||||
|
|
||||||
|
var graph_ul_dl_enabled = this.state.graph_ul_dl_enabled;
|
||||||
|
var graph_ul_dl_width = this.state.graph_ul_dl_width;
|
||||||
|
var graph_ping_enabled = this.state.graph_ping_enabled;
|
||||||
|
var graph_ping_width = this.state.graph_ping_width;
|
||||||
|
var graph_failure_enabled = this.state.graph_failure_enabled;
|
||||||
|
var graph_failure_width = this.state.graph_failure_width;
|
||||||
|
|
||||||
|
var dlClasses = 'my-2 home-graph ';
|
||||||
|
var pingClasses = 'my-2 home-graph ';
|
||||||
|
var failureClasses = 'my-2 home-graph ';
|
||||||
|
|
||||||
|
if(graph_ul_dl_enabled == true) {
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
dlClasses += 'd-none ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(graph_ping_enabled == true) {
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
pingClasses += 'd-none ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(graph_failure_enabled == true) {
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
failureClasses += 'd-none ';
|
||||||
|
}
|
||||||
|
|
||||||
if(loading) {
|
if(loading) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -180,31 +317,43 @@ export default class HistoryGraph extends Component {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Container className="mb-4 mt-1" fluid>
|
<Container className="mb-4 mt-1" fluid>
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col
|
<Col
|
||||||
lg={{ span: 6 }}
|
lg={{ span: graph_ul_dl_width }}
|
||||||
md={{ span: 12 }}
|
md={{ span: graph_ul_dl_width }}
|
||||||
sm={{ span: 12 }}
|
sm={{ span: 12 }}
|
||||||
xs={{ span: 12 }}
|
xs={{ span: 12 }}
|
||||||
className="my-2"
|
className={dlClasses}
|
||||||
>
|
>
|
||||||
<Card className="shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Line data={duData} options={duOptions} />
|
<Line data={duData} options={duOptions} height={440} />
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col
|
<Col
|
||||||
lg={{ span: 6 }}
|
lg={{ span: graph_ping_width }}
|
||||||
md={{ span: 12 }}
|
md={{ span: graph_ping_width }}
|
||||||
sm={{ span: 12 }}
|
sm={{ span: 12 }}
|
||||||
xs={{ span: 12 }}
|
xs={{ span: 12 }}
|
||||||
className="my-2"
|
className={pingClasses}
|
||||||
>
|
>
|
||||||
<Card className="shadow-sm">
|
<Card className="shadow-sm">
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Line data={pingData} options={pingOptions} />
|
<Line data={pingData} options={pingOptions} height={440} />
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
<Col
|
||||||
|
lg={{ span: graph_failure_width }}
|
||||||
|
md={{ span: graph_failure_width }}
|
||||||
|
sm={{ span: 12 }}
|
||||||
|
xs={{ span: 12 }}
|
||||||
|
className={failureClasses}
|
||||||
|
>
|
||||||
|
<Card className="shadow-sm">
|
||||||
|
<Card.Body>
|
||||||
|
<Line data={failData} options={pingOptions} height={440} />
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -217,7 +366,6 @@ export default class HistoryGraph extends Component {
|
|||||||
<Form.Control id="duDaysInput" className="d-inline-block mx-2" defaultValue={days} onInput={this.updateDays}></Form.Control>
|
<Form.Control id="duDaysInput" className="d-inline-block mx-2" defaultValue={days} onInput={this.updateDays}></Form.Control>
|
||||||
<h4 className="d-inline mb-0">days</h4>
|
<h4 className="d-inline mb-0">days</h4>
|
||||||
</div>
|
</div>
|
||||||
{/* <p className="text-muted">This data refreshes every 10 seconds</p> */}
|
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
22
resources/js/components/Home/SettingWithModal.js
vendored
22
resources/js/components/Home/SettingWithModal.js
vendored
@@ -116,7 +116,7 @@ export default class SettingWithModal extends Component {
|
|||||||
<Row key={e.obj.id} className="d-flex align-items-center">
|
<Row key={e.obj.id} className="d-flex align-items-center">
|
||||||
<Col md={{ span: 6 }} sm={{ span: 12 }}>
|
<Col md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
<Form.Group controlId={e.obj.name}>
|
<Form.Group controlId={e.obj.name}>
|
||||||
<Form.Check type="checkbox" label={name} defaultChecked={e.obj.value} onInput={this.updateValue} />
|
<Form.Check type="checkbox" label={name} defaultChecked={Boolean(Number(e.obj.value))} onInput={this.updateValue} />
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={{ span: 6 }} sm={{ span: 12 }}>
|
<Col md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
@@ -138,6 +138,26 @@ export default class SettingWithModal extends Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
} else if(e.type == 'select') {
|
||||||
|
return (
|
||||||
|
<Row key={e.obj.id}>
|
||||||
|
<Col md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
|
<Form.Group controlId={e.obj.name}>
|
||||||
|
<Form.Label>{name}</Form.Label>
|
||||||
|
<Form.Control as="select" defaultValue={e.obj.value} onInput={this.updateValue}>
|
||||||
|
{e.options.map((e,i) => {
|
||||||
|
return (
|
||||||
|
<option key={i} value={e.value}>{e.name}</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Form.Control>
|
||||||
|
</Form.Group>
|
||||||
|
</Col>
|
||||||
|
<Col md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
|
<p>{e.obj.description}</p>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
<Button variant="primary" type="submit" onClick={this.update} >Save</Button>
|
<Button variant="primary" type="submit" onClick={this.update} >Save</Button>
|
||||||
|
|||||||
58
resources/js/components/Home/Settings.js
vendored
58
resources/js/components/Home/Settings.js
vendored
@@ -60,6 +60,64 @@ export default class Settings extends Component {
|
|||||||
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
<Setting name={e.server.name} value={e.server.value} description={e.server.description} />
|
<Setting name={e.server.name} value={e.server.value} description={e.server.description} />
|
||||||
</Col>
|
</Col>
|
||||||
|
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
|
<SettingWithModal title="Graph settings" description="Control settings for the graphs." settings={[
|
||||||
|
{
|
||||||
|
obj: e.download_upload_graph_enabled,
|
||||||
|
type: 'checkbox'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: e.download_upload_graph_width,
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Full-width',
|
||||||
|
'value': 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Half-width',
|
||||||
|
'value': 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: e.ping_graph_enabled,
|
||||||
|
type: 'checkbox'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: e.ping_graph_width,
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Full-width',
|
||||||
|
'value': 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Half-width',
|
||||||
|
'value': 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: e.failure_graph_enabled,
|
||||||
|
type: 'checkbox'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: e.failure_graph_width,
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Full-width',
|
||||||
|
'value': 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Half-width',
|
||||||
|
'value': 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]} />
|
||||||
|
</Col>
|
||||||
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
<Col lg={{ span: 4 }} md={{ span: 6 }} sm={{ span: 12 }}>
|
||||||
<SettingWithModal title="Notification settings" description="Control which types of notifications the server sends." settings={[
|
<SettingWithModal title="Notification settings" description="Control which types of notifications the server sends." settings={[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ Route::group([
|
|||||||
->name('speedtest.latest');
|
->name('speedtest.latest');
|
||||||
Route::get('time/{time}', 'SpeedtestController@time')
|
Route::get('time/{time}', 'SpeedtestController@time')
|
||||||
->name('speedtest.time');
|
->name('speedtest.time');
|
||||||
|
Route::get('fail/{time}', 'SpeedtestController@fail')
|
||||||
|
->name('speedtest.fail');
|
||||||
Route::get('run', 'SpeedtestController@run')
|
Route::get('run', 'SpeedtestController@run')
|
||||||
->name('speedtest.run');
|
->name('speedtest.run');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user