mirror of
https://github.com/henrywhitaker3/Speedtest-Tracker.git
synced 2025-12-21 13:23:04 +01:00
Moved speedtest logic into interface
This commit is contained in:
35
app/Actions/QueueSpeedtest.php
Normal file
35
app/Actions/QueueSpeedtest.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Jobs\SpeedtestJob;
|
||||||
|
use Henrywhitaker3\LaravelActions\Interfaces\ActionInterface;
|
||||||
|
|
||||||
|
class QueueSpeedtest implements ActionInterface
|
||||||
|
{
|
||||||
|
private SpeedtestProvider $speedtestProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new action instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(SpeedtestProvider $speedtestProvider)
|
||||||
|
{
|
||||||
|
$this->speedtestProvider = $speedtestProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the action.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
SettingsHelper::loadIntegrationConfig();
|
||||||
|
|
||||||
|
SpeedtestJob::dispatch(false, config('integrations'), $this->speedtestProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/Exceptions/SpeedtestFailureException.php
Normal file
10
app/Exceptions/SpeedtestFailureException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class SpeedtestFailureException extends Exception
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Helpers;
|
namespace App\Helpers;
|
||||||
|
|
||||||
use App\Speedtest;
|
use App\Speedtest;
|
||||||
|
use App\Utils\OoklaTester;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Henrywhitaker3\Healthchecks\Healthchecks;
|
use Henrywhitaker3\Healthchecks\Healthchecks;
|
||||||
@@ -13,7 +14,8 @@ use Illuminate\Support\Facades\Log;
|
|||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
|
|
||||||
class SpeedtestHelper {
|
class SpeedtestHelper
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs/processes speedtest output to created a Speedtest object
|
* Runs/processes speedtest output to created a Speedtest object
|
||||||
@@ -21,79 +23,10 @@ class SpeedtestHelper {
|
|||||||
* @param boolean|string $output If false, new speedtest runs. If anything else, will try to parse as JSON for speedtest results.
|
* @param boolean|string $output If false, new speedtest runs. If anything else, will try to parse as JSON for speedtest results.
|
||||||
* @return \App\Speedtest|bool
|
* @return \App\Speedtest|bool
|
||||||
*/
|
*/
|
||||||
public static function runSpeedtest($output = false, $scheduled = true)
|
public static function runSpeedtest()
|
||||||
{
|
{
|
||||||
if($output === false) {
|
$tester = new OoklaTester();
|
||||||
$output = SpeedtestHelper::output();
|
return $tester->run();
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$output = json_decode($output, true, 512, JSON_THROW_ON_ERROR);
|
|
||||||
|
|
||||||
if(!SpeedtestHelper::checkOutputIsComplete($output)) {
|
|
||||||
$test = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$test = Speedtest::create([
|
|
||||||
'ping' => $output['ping']['latency'],
|
|
||||||
'download' => SpeedtestHelper::convert($output['download']['bandwidth']),
|
|
||||||
'upload' => SpeedtestHelper::convert($output['upload']['bandwidth']),
|
|
||||||
'server_id' => $output['server']['id'],
|
|
||||||
'server_name' => $output['server']['name'],
|
|
||||||
'server_host' => $output['server']['host'] . ':' . $output['server']['port'],
|
|
||||||
'url' => $output['result']['url'],
|
|
||||||
'scheduled' => $scheduled
|
|
||||||
]);
|
|
||||||
} catch(JsonException $e) {
|
|
||||||
Log::error('Failed to parse speedtest JSON');
|
|
||||||
Log::error($output);
|
|
||||||
$test = false;
|
|
||||||
} catch(Exception $e) {
|
|
||||||
Log::error($e->getMessage());
|
|
||||||
$test = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($test == false) {
|
|
||||||
Speedtest::create([
|
|
||||||
'ping' => 0,
|
|
||||||
'upload' => 0,
|
|
||||||
'download' => 0,
|
|
||||||
'failed' => true,
|
|
||||||
'scheduled' => $scheduled,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cache::flush();
|
|
||||||
|
|
||||||
return $test;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the output of executing speedtest binary.
|
|
||||||
*
|
|
||||||
* @return boolean|string
|
|
||||||
*/
|
|
||||||
public static function output()
|
|
||||||
{
|
|
||||||
$server = SettingsHelper::get('server')['value'];
|
|
||||||
|
|
||||||
$binPath = app_path() . DIRECTORY_SEPARATOR . 'Bin' . DIRECTORY_SEPARATOR . 'speedtest';
|
|
||||||
$homePrefix = config('speedtest.home') . ' && ';
|
|
||||||
|
|
||||||
if($server != '' && $server != false) {
|
|
||||||
$server = explode(',', $server);
|
|
||||||
$server = $server[array_rand($server)];
|
|
||||||
if($server == false) {
|
|
||||||
Log::error('Speedtest server undefined');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return shell_exec($homePrefix . $binPath . ' -f json -s ' . $server);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shell_exec($homePrefix . $binPath . ' -f json');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,10 +38,10 @@ class SpeedtestHelper {
|
|||||||
{
|
{
|
||||||
$t = Carbon::now()->subDay();
|
$t = Carbon::now()->subDay();
|
||||||
$s = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
$s = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
||||||
->where('created_at', '>=', $t)
|
->where('created_at', '>=', $t)
|
||||||
->where('failed', false)
|
->where('failed', false)
|
||||||
->first()
|
->first()
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
@@ -119,8 +52,9 @@ class SpeedtestHelper {
|
|||||||
* @param int|float $bytes
|
* @param int|float $bytes
|
||||||
* @return int|float
|
* @return int|float
|
||||||
*/
|
*/
|
||||||
public static function convert($bytes) {
|
public static function convert($bytes)
|
||||||
return ( $bytes * 8 ) / 1000000;
|
{
|
||||||
|
return ($bytes * 8) / 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,7 +66,7 @@ class SpeedtestHelper {
|
|||||||
{
|
{
|
||||||
$data = Speedtest::latest()->get();
|
$data = Speedtest::latest()->get();
|
||||||
|
|
||||||
if($data->isEmpty()) {
|
if ($data->isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +86,7 @@ class SpeedtestHelper {
|
|||||||
$val = (float)$input[0];
|
$val = (float)$input[0];
|
||||||
$unit = explode('/', $input[1])[0];
|
$unit = explode('/', $input[1])[0];
|
||||||
|
|
||||||
switch($unit) {
|
switch ($unit) {
|
||||||
case 'Mbyte':
|
case 'Mbyte':
|
||||||
$val = $val * 8;
|
$val = $val * 8;
|
||||||
break;
|
break;
|
||||||
@@ -173,48 +107,6 @@ class SpeedtestHelper {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that the speedtest JSON output is complete/valid
|
|
||||||
*
|
|
||||||
* @param array $output
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public static function checkOutputIsComplete($output)
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Array of indexes that must exist in $output
|
|
||||||
*/
|
|
||||||
$checks = [
|
|
||||||
'type' => 'result',
|
|
||||||
'download' => [ 'bandwidth' => '*' ],
|
|
||||||
'upload' => [ 'bandwidth' => '*' ],
|
|
||||||
'ping' => [ 'latency' => '*' ],
|
|
||||||
'server' => [
|
|
||||||
'id' => '*',
|
|
||||||
'name' => '*',
|
|
||||||
'host' => '*',
|
|
||||||
'port' => '*'
|
|
||||||
],
|
|
||||||
'result' => [
|
|
||||||
'url' => '*'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
/**
|
|
||||||
* Array of indexes that must not exist
|
|
||||||
*/
|
|
||||||
$checkMissing = [
|
|
||||||
'type' => 'error'
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach($checks as $key => $value) {
|
|
||||||
if(!isset($output[$key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a percentage rate of failure by days
|
* Get a percentage rate of failure by days
|
||||||
*
|
*
|
||||||
@@ -228,7 +120,7 @@ class SpeedtestHelper {
|
|||||||
$range = [
|
$range = [
|
||||||
Carbon::today()
|
Carbon::today()
|
||||||
];
|
];
|
||||||
for($i = 0; $i < ($days - 1); $i++) {
|
for ($i = 0; $i < ($days - 1); $i++) {
|
||||||
$prev = end($range);
|
$prev = end($range);
|
||||||
$new = $prev->copy()->subDays(1);
|
$new = $prev->copy()->subDays(1);
|
||||||
array_push($range, $new);
|
array_push($range, $new);
|
||||||
@@ -236,7 +128,7 @@ class SpeedtestHelper {
|
|||||||
|
|
||||||
$rate = [];
|
$rate = [];
|
||||||
|
|
||||||
foreach($range as $day) {
|
foreach ($range as $day) {
|
||||||
$success = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', false)->get()[0]['rate'];
|
$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'];
|
$fail = Speedtest::select(DB::raw('COUNT(id) as rate'))->whereDate('created_at', $day)->where('failed', true)->get()[0]['rate'];
|
||||||
|
|
||||||
@@ -260,14 +152,14 @@ class SpeedtestHelper {
|
|||||||
*/
|
*/
|
||||||
public static function dbBackup()
|
public static function dbBackup()
|
||||||
{
|
{
|
||||||
if(env('DB_CONNECTION') === 'sqlite') {
|
if (env('DB_CONNECTION') === 'sqlite') {
|
||||||
if(env('DB_DATABASE') !== null) {
|
if (env('DB_DATABASE') !== null) {
|
||||||
$current = env('DB_DATABASE');
|
$current = env('DB_DATABASE');
|
||||||
try {
|
try {
|
||||||
if(File::copy($current, $current . '.bak')) {
|
if (File::copy($current, $current . '.bak')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}catch(Exception $e) {
|
} catch (Exception $e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,8 +181,8 @@ class SpeedtestHelper {
|
|||||||
|
|
||||||
SpeedtestHelper::dbBackup();
|
SpeedtestHelper::dbBackup();
|
||||||
|
|
||||||
if(sizeof(Speedtest::whereNotNull('id')->get()) > 0) {
|
if (sizeof(Speedtest::whereNotNull('id')->get()) > 0) {
|
||||||
if(Speedtest::whereNotNull('id')->delete()) {
|
if (Speedtest::whereNotNull('id')->delete()) {
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
];
|
];
|
||||||
@@ -311,31 +203,31 @@ class SpeedtestHelper {
|
|||||||
*/
|
*/
|
||||||
public static function testIsLowerThanThreshold(String $type, Speedtest $test)
|
public static function testIsLowerThanThreshold(String $type, Speedtest $test)
|
||||||
{
|
{
|
||||||
if($type == 'percentage') {
|
if ($type == 'percentage') {
|
||||||
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
||||||
->where('failed', false)
|
->where('failed', false)
|
||||||
->get()
|
->get()
|
||||||
->toArray()[0];
|
->toArray()[0];
|
||||||
|
|
||||||
$threshold = SettingsHelper::get('threshold_alert_percentage')->value;
|
$threshold = SettingsHelper::get('threshold_alert_percentage')->value;
|
||||||
|
|
||||||
if($threshold == '') {
|
if ($threshold == '') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
foreach($avg as $key => $value) {
|
foreach ($avg as $key => $value) {
|
||||||
if($key == 'ping') {
|
if ($key == 'ping') {
|
||||||
$threshold = (float)$value * (1 + ( $threshold / 100 ));
|
$threshold = (float)$value * (1 + ($threshold / 100));
|
||||||
|
|
||||||
if($test->$key > $threshold) {
|
if ($test->$key > $threshold) {
|
||||||
array_push($errors, $key);
|
array_push($errors, $key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$threshold = (float)$value * (1 - ( $threshold / 100 ));
|
$threshold = (float)$value * (1 - ($threshold / 100));
|
||||||
|
|
||||||
if($test->$key < $threshold) {
|
if ($test->$key < $threshold) {
|
||||||
array_push($errors, $key);
|
array_push($errors, $key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,7 +236,7 @@ class SpeedtestHelper {
|
|||||||
return $errors;
|
return $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($type == 'absolute') {
|
if ($type == 'absolute') {
|
||||||
$thresholds = [
|
$thresholds = [
|
||||||
'download' => SettingsHelper::get('threshold_alert_absolute_download')->value,
|
'download' => SettingsHelper::get('threshold_alert_absolute_download')->value,
|
||||||
'upload' => SettingsHelper::get('threshold_alert_absolute_upload')->value,
|
'upload' => SettingsHelper::get('threshold_alert_absolute_upload')->value,
|
||||||
@@ -353,17 +245,17 @@ class SpeedtestHelper {
|
|||||||
|
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
foreach($thresholds as $key => $value) {
|
foreach ($thresholds as $key => $value) {
|
||||||
if($value == '') {
|
if ($value == '') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if($key == 'ping') {
|
if ($key == 'ping') {
|
||||||
if($test->$key > $value) {
|
if ($test->$key > $value) {
|
||||||
array_push($errors, $key);
|
array_push($errors, $key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if($test->$key < $value) {
|
if ($test->$key < $value) {
|
||||||
array_push($errors, $key);
|
array_push($errors, $key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\GetLatestSpeedtestData;
|
||||||
|
use App\Actions\GetSpeedtestTimeData;
|
||||||
|
use App\Actions\QueueSpeedtest;
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
use App\Jobs\SpeedtestJob;
|
use App\Jobs\SpeedtestJob;
|
||||||
@@ -61,21 +64,7 @@ class SpeedtestController extends Controller
|
|||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ttl = Carbon::now()->addDays(1);
|
$data = run(GetSpeedtestTimeData::class, $days);
|
||||||
$data = Cache::remember('speedtest-days-' . $days, $ttl, function () use ($days) {
|
|
||||||
$showFailed = (bool)SettingsHelper::get('show_failed_tests_on_graph')->value;
|
|
||||||
|
|
||||||
if ($showFailed === true) {
|
|
||||||
return Speedtest::where('created_at', '>=', Carbon::now()->subDays($days))
|
|
||||||
->orderBy('created_at', 'asc')
|
|
||||||
->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Speedtest::where('created_at', '>=', Carbon::now()->subDays($days))
|
|
||||||
->where('failed', false)
|
|
||||||
->orderBy('created_at', 'asc')
|
|
||||||
->get();
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get speedtests in last x days',
|
'method' => 'get speedtests in last x days',
|
||||||
@@ -121,39 +110,10 @@ class SpeedtestController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function latest()
|
public function latest()
|
||||||
{
|
{
|
||||||
$data = SpeedtestHelper::latest();
|
$data = run(GetLatestSpeedtestData::class);
|
||||||
|
|
||||||
$response = [
|
if ($data['data']) {
|
||||||
'method' => 'get latest speedtest',
|
return response()->json($data, 200);
|
||||||
'data' => $data,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (SettingsHelper::get('show_average')) {
|
|
||||||
$avg = Speedtest::select(DB::raw('AVG(ping) as ping, AVG(download) as download, AVG(upload) as upload'))
|
|
||||||
->where('failed', false)
|
|
||||||
->first()
|
|
||||||
->toArray();
|
|
||||||
$response['average'] = $avg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SettingsHelper::get('show_max')) {
|
|
||||||
$max = Speedtest::select(DB::raw('MAX(ping) as ping, MAX(download) as download, MAX(upload) as upload'))
|
|
||||||
->where('failed', false)
|
|
||||||
->first()
|
|
||||||
->toArray();
|
|
||||||
$response['maximum'] = $max;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SettingsHelper::get('show_min')) {
|
|
||||||
$min = Speedtest::select(DB::raw('MIN(ping) as ping, MIN(download) as download, MIN(upload) as upload'))
|
|
||||||
->where('failed', false)
|
|
||||||
->first()
|
|
||||||
->toArray();
|
|
||||||
$response['minimum'] = $min;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($data) {
|
|
||||||
return response()->json($response, 200);
|
|
||||||
} else {
|
} else {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'get latest speedtest',
|
'method' => 'get latest speedtest',
|
||||||
@@ -170,8 +130,8 @@ class SpeedtestController extends Controller
|
|||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
SettingsHelper::loadIntegrationConfig();
|
run(QueueSpeedtest::class);
|
||||||
$data = SpeedtestJob::dispatch(false, config('integrations'));
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'method' => 'run speedtest',
|
'method' => 'run speedtest',
|
||||||
'data' => 'a new speedtest has been added to the queue'
|
'data' => 'a new speedtest has been added to the queue'
|
||||||
|
|||||||
11
app/Interfaces/SpeedtestProvider.php
Normal file
11
app/Interfaces/SpeedtestProvider.php
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Interfaces;
|
||||||
|
|
||||||
|
use App\Speedtest;
|
||||||
|
|
||||||
|
interface SpeedtestProvider
|
||||||
|
{
|
||||||
|
public function run(): Speedtest;
|
||||||
|
public function output();
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ use App\Events\SpeedtestCompleteEvent;
|
|||||||
use App\Events\SpeedtestFailedEvent;
|
use App\Events\SpeedtestFailedEvent;
|
||||||
use App\Helpers\SettingsHelper;
|
use App\Helpers\SettingsHelper;
|
||||||
use App\Helpers\SpeedtestHelper;
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Utils\OoklaTester;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Healthcheck;
|
use Healthcheck;
|
||||||
use Henrywhitaker3\Healthchecks\Healthchecks;
|
use Henrywhitaker3\Healthchecks\Healthchecks;
|
||||||
@@ -34,15 +36,18 @@ class SpeedtestJob implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
|
private SpeedtestProvider $speedtestProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct($scheduled = true, $config = [])
|
public function __construct($scheduled = true, $config = [], SpeedtestProvider $speedtestProvider)
|
||||||
{
|
{
|
||||||
$this->scheduled = $scheduled;
|
$this->scheduled = $scheduled;
|
||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
|
$this->speedtestProvider = $speedtestProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,8 +60,8 @@ class SpeedtestJob implements ShouldQueue
|
|||||||
if ($this->config['healthchecks_enabled'] === true) {
|
if ($this->config['healthchecks_enabled'] === true) {
|
||||||
$this->healthcheck('start');
|
$this->healthcheck('start');
|
||||||
}
|
}
|
||||||
$output = SpeedtestHelper::output();
|
$output = $this->speedtestProvider->output();
|
||||||
$speedtest = SpeedtestHelper::runSpeedtest($output, $this->scheduled);
|
$speedtest = $this->speedtestProvider->run($output, $this->scheduled);
|
||||||
if ($speedtest == false) {
|
if ($speedtest == false) {
|
||||||
if ($this->config['healthchecks_enabled'] === true) {
|
if ($this->config['healthchecks_enabled'] === true) {
|
||||||
$this->healthcheck('fail');
|
$this->healthcheck('fail');
|
||||||
|
|||||||
34
app/Providers/SpeedtestServiceProvider.php
Normal file
34
app/Providers/SpeedtestServiceProvider.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Utils\OoklaTester;
|
||||||
|
use File;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Schema;
|
||||||
|
|
||||||
|
class SpeedtestServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bootstrap services.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
if (File::exists(env('DB_DATABASE'))) {
|
||||||
|
if (Schema::hasTable('settings')) {
|
||||||
|
switch (SettingsHelper::get('speedtest_provider')) {
|
||||||
|
case 'ookla':
|
||||||
|
default:
|
||||||
|
$this->app->singleton(SpeedtestProvider::class, function () {
|
||||||
|
return new OoklaTester();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
128
app/Utils/OoklaTester.php
Normal file
128
app/Utils/OoklaTester.php
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Utils;
|
||||||
|
|
||||||
|
use App\Exceptions\SpeedtestFailureException;
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Helpers\SpeedtestHelper;
|
||||||
|
use App\Interfaces\SpeedtestProvider;
|
||||||
|
use App\Speedtest;
|
||||||
|
use Cache;
|
||||||
|
use Exception;
|
||||||
|
use JsonException;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
class OoklaTester implements SpeedtestProvider
|
||||||
|
{
|
||||||
|
public function run($output = false, $scheduled = true): Speedtest
|
||||||
|
{
|
||||||
|
if ($output === false) {
|
||||||
|
$output = $this->output();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$output = json_decode($output, true, 512, JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
if (!$this->isOutputIsComplete($output)) {
|
||||||
|
$test = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$test = Speedtest::create([
|
||||||
|
'ping' => $output['ping']['latency'],
|
||||||
|
'download' => SpeedtestHelper::convert($output['download']['bandwidth']),
|
||||||
|
'upload' => SpeedtestHelper::convert($output['upload']['bandwidth']),
|
||||||
|
'server_id' => $output['server']['id'],
|
||||||
|
'server_name' => $output['server']['name'],
|
||||||
|
'server_host' => $output['server']['host'] . ':' . $output['server']['port'],
|
||||||
|
'url' => $output['result']['url'],
|
||||||
|
'scheduled' => $scheduled
|
||||||
|
]);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
Log::error('Failed to parse speedtest JSON');
|
||||||
|
Log::error($output);
|
||||||
|
$test = false;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
$test = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($test == false) {
|
||||||
|
Speedtest::create([
|
||||||
|
'ping' => 0,
|
||||||
|
'upload' => 0,
|
||||||
|
'download' => 0,
|
||||||
|
'failed' => true,
|
||||||
|
'scheduled' => $scheduled,
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw new SpeedtestFailureException($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::flush();
|
||||||
|
|
||||||
|
return $test;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function output()
|
||||||
|
{
|
||||||
|
$server = SettingsHelper::get('server')['value'];
|
||||||
|
|
||||||
|
$binPath = app_path() . DIRECTORY_SEPARATOR . 'Bin' . DIRECTORY_SEPARATOR . 'speedtest';
|
||||||
|
$homePrefix = config('speedtest.home') . ' && ';
|
||||||
|
|
||||||
|
if ($server != '' && $server != false) {
|
||||||
|
$server = explode(',', $server);
|
||||||
|
$server = $server[array_rand($server)];
|
||||||
|
if ($server == false) {
|
||||||
|
Log::error('Speedtest server undefined');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shell_exec($homePrefix . $binPath . ' -f json -s ' . $server);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shell_exec($homePrefix . $binPath . ' -f json');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the speedtest JSON output is complete/valid
|
||||||
|
*
|
||||||
|
* @param array $output
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
private static function isOutputIsComplete($output)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Array of indexes that must exist in $output
|
||||||
|
*/
|
||||||
|
$checks = [
|
||||||
|
'type' => 'result',
|
||||||
|
'download' => ['bandwidth' => '*'],
|
||||||
|
'upload' => ['bandwidth' => '*'],
|
||||||
|
'ping' => ['latency' => '*'],
|
||||||
|
'server' => [
|
||||||
|
'id' => '*',
|
||||||
|
'name' => '*',
|
||||||
|
'host' => '*',
|
||||||
|
'port' => '*'
|
||||||
|
],
|
||||||
|
'result' => [
|
||||||
|
'url' => '*'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Array of indexes that must not exist
|
||||||
|
*/
|
||||||
|
$checkMissing = [
|
||||||
|
'type' => 'error'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($checks as $key => $value) {
|
||||||
|
if (!isset($output[$key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -182,6 +182,7 @@ return [
|
|||||||
* Custom providers...
|
* Custom providers...
|
||||||
*/
|
*/
|
||||||
App\Providers\IntegrationsServiceProvider::class,
|
App\Providers\IntegrationsServiceProvider::class,
|
||||||
|
App\Providers\SpeedtestServiceProvider::class,
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Helpers\SettingsHelper;
|
||||||
|
use App\Setting;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddSpeedtestProviderSetting extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if (!SettingsHelper::get('healthchecks_server_url')) {
|
||||||
|
Setting::create([
|
||||||
|
'name' => 'speedtest_provider',
|
||||||
|
'value' => 'ookla',
|
||||||
|
'description' => 'The provider/package used to run speedtests.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Setting::whereIn('name', [
|
||||||
|
'speedtest_provider',
|
||||||
|
])->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user