diff --git a/app/Console/Commands/TestInfluxConnection.php b/app/Console/Commands/TestInfluxConnection.php new file mode 100644 index 00000000..ae789eeb --- /dev/null +++ b/app/Console/Commands/TestInfluxConnection.php @@ -0,0 +1,62 @@ +enabled = (bool) SettingsHelper::get('influx_db_enabled')->value; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + if (!$this->enabled) { + $this->warn('InfluxDB is not enabled'); + exit; + } + + try { + InfluxDB::connect(); + $this->info('Connected successfully'); + } catch (InfluxDBConnectionErrorException $e) { + $this->error('Couldn\'t connect'); + return 1; + } + + return 0; + } +} diff --git a/app/Exceptions/InfluxDBConnectionErrorException.php b/app/Exceptions/InfluxDBConnectionErrorException.php new file mode 100644 index 00000000..24266020 --- /dev/null +++ b/app/Exceptions/InfluxDBConnectionErrorException.php @@ -0,0 +1,10 @@ +store($speedtest); + } catch (InfluxDBNotEnabledException $e) { + info('not enabled'); + } catch (Exception $e) { + Log::error($e); + } + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index b33e65d5..25c07449 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -10,6 +10,8 @@ use App\Listeners\SpeedtestCompleteListener; use App\Listeners\SpeedtestFailedListener; use App\Listeners\SpeedtestOverviewListener; use App\Listeners\TestNotificationListener; +use App\Observers\SpeedtestObserver; +use App\Speedtest; use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Listeners\SendEmailVerificationNotification; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; @@ -49,6 +51,6 @@ class EventServiceProvider extends ServiceProvider { parent::boot(); - // + Speedtest::observe(SpeedtestObserver::class); } } diff --git a/app/Speedtest.php b/app/Speedtest.php index f0bc85ad..f7dbe02e 100644 --- a/app/Speedtest.php +++ b/app/Speedtest.php @@ -25,4 +25,18 @@ class Speedtest extends Model ]; protected $table = 'speedtests'; + + public function formatForInfluxDB() + { + return [ + 'id' => (int) $this->id, + 'download' => (float) $this->download, + 'upload' => (float) $this->upload, + 'ping' => (float) $this->ping, + 'server_id' => (int) $this->server_id, + 'server_host' => $this->server_host, + 'server_name' => $this->server_name, + 'scheduled' => (bool) $this->scheduled, + ]; + } } diff --git a/app/Utils/InfluxDB/InfluxDB.php b/app/Utils/InfluxDB/InfluxDB.php new file mode 100644 index 00000000..ebe9a00b --- /dev/null +++ b/app/Utils/InfluxDB/InfluxDB.php @@ -0,0 +1,104 @@ +client = $client; + } + + /** + * Connect to influx db + * + * @param string $host + * @param integer $port + * @param string $database + * @return InfluxDB + */ + public static function connect() + { + if (!(bool) SettingsHelper::get('influx_db_enabled')->value) { + throw new InfluxDBNotEnabledException(); + } + + $host = SettingsHelper::get('influx_db_host')->value; + $port = SettingsHelper::get('influx_db_port')->value; + $token = ''; + $database = SettingsHelper::get('influx_db_database')->value; + $version = (int) SettingsHelper::get('influx_db_version')->value; + + $wrapper = $version === 1 + ? new InfluxDBVersion1Wrapper( + new Version1(str_replace(['http://', 'https://'], '', $host), $port) + ) + : new InfluxDBVersion2Wrapper( + new Version2([]) + ); + + return (new self($wrapper))->setDatabase($database) + ->testConnection(); + } + + /** + * Set the database field + * + * @param string $database + * @return InfluxDB + */ + public function setDatabase(string $database): InfluxDB + { + $this->database = $database; + + return $this; + } + + /** + * Test the connection + * + * @throws InfluxDBConnectionErrorException + * @return InfluxDB + */ + public function testConnection(): InfluxDB + { + if (!$this->client->testConnection()) { + throw new InfluxDBConnectionErrorException(); + } + + if (!$this->doesDatabaseExist()) { + $this->createDatabase(); + } + + return $this; + } + + public function doesDatabaseExist(): bool + { + return $this->client->doesDatabaseExist($this->database); + } + + public function createDatabase(): bool + { + return $this->client->createDatabase($this->database); + } + + public function store(Speedtest $speedtest): InfluxDB + { + $this->client->store($speedtest); + + return $this; + } +} diff --git a/app/Utils/InfluxDB/InfluxDBVersion1Wrapper.php b/app/Utils/InfluxDB/InfluxDBVersion1Wrapper.php new file mode 100644 index 00000000..7f4cabec --- /dev/null +++ b/app/Utils/InfluxDB/InfluxDBVersion1Wrapper.php @@ -0,0 +1,71 @@ +client = $client; + } + + public function testConnection(): bool + { + try { + $this->client->listDatabases(); + + return true; + } catch (Exception $e) { + Log::error($e); + + return false; + } + } + + public function doesDatabaseExist(string $database): bool + { + $this->database = $this->client->selectDB($database); + + return (bool) $this->database->exists(); + } + + public function createDatabase(string $database): bool + { + try { + $this->database->create(new RetentionPolicy( + 'speedtest_retention_policy', + config('services.influxdb.retention'), + 1, + true + )); + + return true; + } catch (Exception $e) { + Log::error($e); + return false; + } + } + + public function store(Speedtest $speedtest): bool + { + return $this->database->writePoints([ + new Point( + 'speedtest', + null, + ['host' => config('services.influxdb.host')], + $speedtest->formatForInfluxDB(), + ) + ]); + } +} diff --git a/app/Utils/InfluxDB/InfluxDBWrapperInterface.php b/app/Utils/InfluxDB/InfluxDBWrapperInterface.php new file mode 100644 index 00000000..3c72c7ea --- /dev/null +++ b/app/Utils/InfluxDB/InfluxDBWrapperInterface.php @@ -0,0 +1,13 @@ +=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.4|^9.1", + "squizlabs/php_codesniffer": "~2.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "InfluxDB2\\": "src/InfluxDB2" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "InfluxDB (v2+) Client Library for PHP", + "homepage": "https://www.github.com/influxdata/influxdb-client-php", + "keywords": [ + "influxdb" + ], + "support": { + "issues": "https://github.com/influxdata/influxdb-client-php/issues", + "source": "https://github.com/influxdata/influxdb-client-php/tree/1.12.0" + }, + "time": "2021-04-01T06:28:57+00:00" + }, + { + "name": "influxdb/influxdb-php", + "version": "1.15.2", + "source": { + "type": "git", + "url": "https://github.com/influxdata/influxdb-php.git", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/influxdata/influxdb-php/zipball/d6e59f4f04ab9107574fda69c2cbe36671253d03", + "reference": "d6e59f4f04ab9107574fda69c2cbe36671253d03", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0|^7.0", + "php": "^5.5 || ^7.0 || ^8.0" + }, + "require-dev": { + "dms/phpunit-arraysubset-asserts": "^0.2.1", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "Curl extension, needed for Curl driver", + "stefanotorresi/influxdb-php-async": "An asyncronous client for InfluxDB, implemented via ReactPHP." + }, + "type": "library", + "autoload": { + "psr-4": { + "InfluxDB\\": "src/InfluxDB" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Hoogendijk", + "email": "stephen@tca0.nl" + }, + { + "name": "Daniel Martinez", + "email": "danimartcas@hotmail.com" + }, + { + "name": "Gianluca Arbezzano", + "email": "gianarb92@gmail.com" + } + ], + "description": "InfluxDB client library for PHP", + "keywords": [ + "client", + "influxdata", + "influxdb", + "influxdb class", + "influxdb client", + "influxdb library", + "time series" + ], + "support": { + "issues": "https://github.com/influxdata/influxdb-php/issues", + "source": "https://github.com/influxdata/influxdb-php/tree/1.15.2" + }, + "time": "2020-12-26T17:45:17+00:00" + }, { "name": "laravel-notification-channels/telegram", "version": "0.5.1", diff --git a/config/services.php b/config/services.php index 88eb96e4..c966d76e 100644 --- a/config/services.php +++ b/config/services.php @@ -34,4 +34,9 @@ return [ 'token' => env('TELEGRAM_BOT_TOKEN', 'YOUR BOT TOKEN HERE') ], + 'influxdb' => [ + 'retention' => env('INFLUXDB_RETENTION', '30d'), + 'host' => env('INFLUXDB_HOST_TAG', 'speedtest'), + ], + ]; diff --git a/database/migrations/2021_04_10_182503_add_influx_db_settings.php b/database/migrations/2021_04_10_182503_add_influx_db_settings.php new file mode 100644 index 00000000..2a1e66eb --- /dev/null +++ b/database/migrations/2021_04_10_182503_add_influx_db_settings.php @@ -0,0 +1,74 @@ + 'influx_db_enabled', + 'value' => false, + 'description' => 'Enable the InfluxDB integration for speedtests.' + ]); + } + + if (!SettingsHelper::get('influx_db_host')) { + Setting::create([ + 'name' => 'influx_db_host', + 'value' => '', + 'description' => 'InfluxDB hostname, include the protocol (http:// or https://).' + ]); + } + + if (!SettingsHelper::get('influx_db_port')) { + Setting::create([ + 'name' => 'influx_db_port', + 'value' => '', + 'description' => 'InfluxDB port' + ]); + } + + if (!SettingsHelper::get('influx_db_database')) { + Setting::create([ + 'name' => 'influx_db_database', + 'value' => '', + 'description' => 'InfluxDB database' + ]); + } + + if (!SettingsHelper::get('influx_db_version')) { + Setting::create([ + 'name' => 'influx_db_version', + 'value' => 1, + 'description' => 'InfluxDB version' + ]); + } + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Setting::whereIn('name', [ + 'influx_db_enabled', + 'influx_db_host', + 'influx_db_port', + 'influx_db_database', + 'influx_db_version', + ])->delete(); + } +}