mirror of
https://github.com/alexjustesen/speedtest-tracker.git
synced 2026-06-23 07:30:09 +00:00
Dev Release v0.16.0 (#1143)
This commit is contained in:
+11
-66
@@ -13,6 +13,7 @@ namespace PHPSTORM_META {
|
||||
*/
|
||||
override(new \Illuminate\Contracts\Container\Container, map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -171,11 +172,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -214,7 +210,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -251,6 +246,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\Illuminate\Container\Container::makeWith(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -409,11 +405,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -452,7 +443,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -489,6 +479,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\Illuminate\Contracts\Container\Container::get(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -647,11 +638,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -690,7 +676,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -727,6 +712,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\Illuminate\Contracts\Container\Container::make(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -885,11 +871,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -928,7 +909,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -965,6 +945,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\Illuminate\Contracts\Container\Container::makeWith(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -1123,11 +1104,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -1166,7 +1142,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -1203,6 +1178,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\App::get(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -1361,11 +1337,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -1404,7 +1375,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -1441,6 +1411,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\App::make(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -1599,11 +1570,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -1642,7 +1608,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -1679,6 +1644,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\App::makeWith(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -1837,11 +1803,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -1880,7 +1841,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -1917,6 +1877,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\app(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -2075,11 +2036,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -2118,7 +2074,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -2155,6 +2110,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\resolve(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -2313,11 +2269,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -2356,7 +2307,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
@@ -2393,6 +2343,7 @@ namespace PHPSTORM_META {
|
||||
]));
|
||||
override(\Psr\Container\ContainerInterface::get(0), map([
|
||||
'' => '@',
|
||||
'App\Settings\DataMigrationSettings' => \App\Settings\DataMigrationSettings::class,
|
||||
'App\Settings\GeneralSettings' => \App\Settings\GeneralSettings::class,
|
||||
'App\Settings\InfluxDbSettings' => \App\Settings\InfluxDbSettings::class,
|
||||
'App\Settings\NotificationSettings' => \App\Settings\NotificationSettings::class,
|
||||
@@ -2551,11 +2502,6 @@ namespace PHPSTORM_META {
|
||||
'Lorisleiva\Actions\ActionManager' => \Lorisleiva\Actions\ActionManager::class,
|
||||
'Lorisleiva\Lody\LodyManager' => \Lorisleiva\Lody\LodyManager::class,
|
||||
'Lorisleiva\Lody\Psr4Resolver' => \Lorisleiva\Lody\Psr4Resolver::class,
|
||||
'Maatwebsite\Excel\Cache\CacheManager' => \Maatwebsite\Excel\Cache\CacheManager::class,
|
||||
'Maatwebsite\Excel\Files\Filesystem' => \Maatwebsite\Excel\Files\Filesystem::class,
|
||||
'Maatwebsite\Excel\Files\TemporaryFileFactory' => \Maatwebsite\Excel\Files\TemporaryFileFactory::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionHandler' => \Maatwebsite\Excel\Transactions\DbTransactionHandler::class,
|
||||
'Maatwebsite\Excel\Transactions\TransactionManager' => \Maatwebsite\Excel\Transactions\TransactionManager::class,
|
||||
'NotificationChannels\Telegram\Telegram' => \NotificationChannels\Telegram\Telegram::class,
|
||||
'NunoMaduro\Collision\Provider' => \NunoMaduro\Collision\Provider::class,
|
||||
'Spatie\FlareClient\Flare' => \Spatie\FlareClient\Flare::class,
|
||||
@@ -2594,7 +2540,6 @@ namespace PHPSTORM_META {
|
||||
'db.transactions' => \Illuminate\Database\DatabaseTransactionsManager::class,
|
||||
'encrypter' => \Illuminate\Encryption\Encrypter::class,
|
||||
'events' => \Illuminate\Events\Dispatcher::class,
|
||||
'excel' => \Maatwebsite\Excel\Excel::class,
|
||||
'filament' => \Filament\FilamentManager::class,
|
||||
'files' => \Illuminate\Filesystem\Filesystem::class,
|
||||
'filesystem' => \Illuminate\Filesystem\FilesystemManager::class,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Speedtest Tracker
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `v0.16.0` includes a breaking change that requires user action for existing installs. Read the [release](https://github.com/alexjustesen/speedtest-tracker/releases/tag/v0.16.0) notes regarding the data migration.
|
||||
|
||||
[](https://star-history.com/#alexjustesen/speedtest-tracker&Date)
|
||||
|
||||
## Introduction
|
||||
|
||||
-311
@@ -17898,231 +17898,6 @@ namespace Lorisleiva\Lody {
|
||||
}
|
||||
}
|
||||
|
||||
namespace Maatwebsite\Excel\Facades {
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/ class Excel {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param object $export
|
||||
* @param string|null $fileName
|
||||
* @param string $writerType
|
||||
* @param array $headers
|
||||
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @static
|
||||
*/ public static function download($export, $fileName, $writerType = null, $headers = [])
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->download($export, $fileName, $writerType, $headers);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string|null $disk Fallback for usage with named properties
|
||||
* @param object $export
|
||||
* @param string $filePath
|
||||
* @param string|null $diskName
|
||||
* @param string $writerType
|
||||
* @param mixed $diskOptions
|
||||
* @return bool
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Exception
|
||||
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
|
||||
* @static
|
||||
*/ public static function store($export, $filePath, $diskName = null, $writerType = null, $diskOptions = [], $disk = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->store($export, $filePath, $diskName, $writerType, $diskOptions, $disk);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param object $export
|
||||
* @param string $filePath
|
||||
* @param string|null $disk
|
||||
* @param string $writerType
|
||||
* @param mixed $diskOptions
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch
|
||||
* @static
|
||||
*/ public static function queue($export, $filePath, $disk = null, $writerType = null, $diskOptions = [])
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->queue($export, $filePath, $disk, $writerType, $diskOptions);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param object $export
|
||||
* @param string $writerType
|
||||
* @return string
|
||||
* @static
|
||||
*/ public static function raw($export, $writerType)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->raw($export, $writerType);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param object $import
|
||||
* @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
|
||||
* @param string|null $disk
|
||||
* @param string|null $readerType
|
||||
* @return \Maatwebsite\Excel\Reader|\Illuminate\Foundation\Bus\PendingDispatch
|
||||
* @static
|
||||
*/ public static function import($import, $filePath, $disk = null, $readerType = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->import($import, $filePath, $disk, $readerType);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param object $import
|
||||
* @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
|
||||
* @param string|null $disk
|
||||
* @param string|null $readerType
|
||||
* @return array
|
||||
* @static
|
||||
*/ public static function toArray($import, $filePath, $disk = null, $readerType = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->toArray($import, $filePath, $disk, $readerType);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param object $import
|
||||
* @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
|
||||
* @param string|null $disk
|
||||
* @param string|null $readerType
|
||||
* @return \Illuminate\Support\Collection
|
||||
* @static
|
||||
*/ public static function toCollection($import, $filePath, $disk = null, $readerType = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->toCollection($import, $filePath, $disk, $readerType);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param \Illuminate\Contracts\Queue\ShouldQueue $import
|
||||
* @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $filePath
|
||||
* @param string|null $disk
|
||||
* @param string $readerType
|
||||
* @return \Illuminate\Foundation\Bus\PendingDispatch
|
||||
* @static
|
||||
*/ public static function queueImport($import, $filePath, $disk = null, $readerType = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Excel $instance */
|
||||
return $instance->queueImport($import, $filePath, $disk, $readerType);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string $concern
|
||||
* @param callable $handler
|
||||
* @param string $event
|
||||
* @static
|
||||
*/ public static function extend($concern, $handler, $event = 'Maatwebsite\\Excel\\Events\\BeforeWriting')
|
||||
{
|
||||
return \Maatwebsite\Excel\Excel::extend($concern, $handler, $event);
|
||||
}
|
||||
/**
|
||||
* When asserting downloaded, stored, queued or imported, use regular expression
|
||||
* to look for a matching file path.
|
||||
*
|
||||
* @return void
|
||||
* @static
|
||||
*/ public static function matchByRegex()
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
$instance->matchByRegex();
|
||||
}
|
||||
/**
|
||||
* When asserting downloaded, stored, queued or imported, use regular string
|
||||
* comparison for matching file path.
|
||||
*
|
||||
* @return void
|
||||
* @static
|
||||
*/ public static function doNotMatchByRegex()
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
$instance->doNotMatchByRegex();
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string $fileName
|
||||
* @param callable|null $callback
|
||||
* @static
|
||||
*/ public static function assertDownloaded($fileName, $callback = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
return $instance->assertDownloaded($fileName, $callback);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string $filePath
|
||||
* @param string|callable|null $disk
|
||||
* @param callable|null $callback
|
||||
* @static
|
||||
*/ public static function assertStored($filePath, $disk = null, $callback = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
return $instance->assertStored($filePath, $disk, $callback);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string $filePath
|
||||
* @param string|callable|null $disk
|
||||
* @param callable|null $callback
|
||||
* @static
|
||||
*/ public static function assertQueued($filePath, $disk = null, $callback = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
return $instance->assertQueued($filePath, $disk, $callback);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @static
|
||||
*/ public static function assertQueuedWithChain($chain)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
return $instance->assertQueuedWithChain($chain);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string $classname
|
||||
* @param callable|null $callback
|
||||
* @static
|
||||
*/ public static function assertExportedInRaw($classname, $callback = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
return $instance->assertExportedInRaw($classname, $callback);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param string $filePath
|
||||
* @param string|callable|null $disk
|
||||
* @param callable|null $callback
|
||||
* @static
|
||||
*/ public static function assertImported($filePath, $disk = null, $callback = null)
|
||||
{
|
||||
/** @var \Maatwebsite\Excel\Fakes\ExcelFake $instance */
|
||||
return $instance->assertImported($filePath, $disk, $callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Spatie\LaravelIgnition\Facades {
|
||||
/**
|
||||
*
|
||||
@@ -18495,41 +18270,6 @@ namespace Illuminate\Support {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
* @implements \ArrayAccess<TKey, TValue>
|
||||
* @implements \Illuminate\Support\Enumerable<TKey, TValue>
|
||||
*/ class Collection {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see \Maatwebsite\Excel\Mixins\DownloadCollectionMixin::downloadExcel()
|
||||
* @param string $fileName
|
||||
* @param string|null $writerType
|
||||
* @param mixed $withHeadings
|
||||
* @param array $responseHeaders
|
||||
* @static
|
||||
*/ public static function downloadExcel($fileName, $writerType = null, $withHeadings = false, $responseHeaders = [])
|
||||
{
|
||||
return \Illuminate\Support\Collection::downloadExcel($fileName, $writerType, $withHeadings, $responseHeaders);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see \Maatwebsite\Excel\Mixins\StoreCollectionMixin::storeExcel()
|
||||
* @param string $filePath
|
||||
* @param string|null $disk
|
||||
* @param string|null $writerType
|
||||
* @param mixed $withHeadings
|
||||
* @static
|
||||
*/ public static function storeExcel($filePath, $disk = null, $writerType = null, $withHeadings = false)
|
||||
{
|
||||
return \Illuminate\Support\Collection::storeExcel($filePath, $disk, $writerType, $withHeadings);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/ class Str {
|
||||
/**
|
||||
*
|
||||
@@ -23138,56 +22878,6 @@ namespace {
|
||||
*/ public static function getRelationWithoutConstraintsProxy($relation)
|
||||
{
|
||||
return \Illuminate\Database\Eloquent\Builder::getRelationWithoutConstraintsProxy($relation);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see \Maatwebsite\Excel\Mixins\DownloadQueryMacro::__invoke()
|
||||
* @param string $fileName
|
||||
* @param string|null $writerType
|
||||
* @param mixed $withHeadings
|
||||
* @static
|
||||
*/ public static function downloadExcel($fileName, $writerType = null, $withHeadings = false)
|
||||
{
|
||||
return \Illuminate\Database\Eloquent\Builder::downloadExcel($fileName, $writerType, $withHeadings);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see \Maatwebsite\Excel\Mixins\StoreQueryMacro::__invoke()
|
||||
* @param string $filePath
|
||||
* @param string|null $disk
|
||||
* @param string|null $writerType
|
||||
* @param mixed $withHeadings
|
||||
* @static
|
||||
*/ public static function storeExcel($filePath, $disk = null, $writerType = null, $withHeadings = false)
|
||||
{
|
||||
return \Illuminate\Database\Eloquent\Builder::storeExcel($filePath, $disk, $writerType, $withHeadings);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see \Maatwebsite\Excel\Mixins\ImportMacro::__invoke()
|
||||
* @param string $filename
|
||||
* @param string|null $disk
|
||||
* @param string|null $readerType
|
||||
* @static
|
||||
*/ public static function import($filename, $disk = null, $readerType = null)
|
||||
{
|
||||
return \Illuminate\Database\Eloquent\Builder::import($filename, $disk, $readerType);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @see \Maatwebsite\Excel\Mixins\ImportAsMacro::__invoke()
|
||||
* @param string $filename
|
||||
* @param callable $mapping
|
||||
* @param string|null $disk
|
||||
* @param string|null $readerType
|
||||
* @static
|
||||
*/ public static function importAs($filename, $mapping, $disk = null, $readerType = null)
|
||||
{
|
||||
return \Illuminate\Database\Eloquent\Builder::importAs($filename, $mapping, $disk, $readerType);
|
||||
}
|
||||
/**
|
||||
* Set the columns to be selected.
|
||||
@@ -25186,7 +24876,6 @@ namespace {
|
||||
class Livewire extends \Livewire\Livewire {}
|
||||
class Action extends \Lorisleiva\Actions\Facades\Actions {}
|
||||
class Lody extends \Lorisleiva\Lody\Lody {}
|
||||
class Excel extends \Maatwebsite\Excel\Facades\Excel {}
|
||||
class Flare extends \Spatie\LaravelIgnition\Facades\Flare {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Models\User;
|
||||
use App\Settings\DataMigrationSettings;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class MigrateBadJsonResults
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public int $jobTimeout = 60 * 5;
|
||||
|
||||
public int $jobTries = 1;
|
||||
|
||||
public function handle(User $user)
|
||||
{
|
||||
$dataSettings = new DataMigrationSettings();
|
||||
|
||||
$tableName = 'results_bad_json';
|
||||
|
||||
if ($dataSettings->bad_json_migrated) {
|
||||
Notification::make()
|
||||
->title('❌ Hmmm it seems someone has already migrated the data!')
|
||||
->body('Check your results table and make sure you\'re not triggering a duplicate data migration.')
|
||||
->danger()
|
||||
->sendToDatabase($user);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! Schema::hasTable('results')) {
|
||||
Notification::make()
|
||||
->title('❌ Could not migrate bad json results!')
|
||||
->body('The "results" table is missing.')
|
||||
->danger()
|
||||
->sendToDatabase($user);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! Schema::hasTable($tableName)) {
|
||||
Notification::make()
|
||||
->title('❌ Could not migrate bad json results!')
|
||||
->body('The "results_bad_json" table is missing.')
|
||||
->danger()
|
||||
->sendToDatabase($user);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy backup data to the new results table and reformat it.
|
||||
*/
|
||||
try {
|
||||
DB::table($tableName)->chunkById(100, function ($results) {
|
||||
foreach ($results as $result) {
|
||||
$record = [
|
||||
'service' => 'ookla',
|
||||
'ping' => $result->ping,
|
||||
'download' => $result->download,
|
||||
'upload' => $result->upload,
|
||||
'comments' => $result->comments,
|
||||
'data' => json_decode($result->data),
|
||||
'status' => match ($result->successful) {
|
||||
1 => ResultStatus::Completed,
|
||||
default => ResultStatus::Failed,
|
||||
},
|
||||
'scheduled' => $result->scheduled,
|
||||
'created_at' => $result->created_at,
|
||||
'updated_at' => now(),
|
||||
];
|
||||
|
||||
DB::table('results')->insert($record);
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
Log::error($e);
|
||||
|
||||
Notification::make()
|
||||
->title('There was an issue migrating the data!')
|
||||
->body('Check the logs for an output of the issue.')
|
||||
->danger()
|
||||
->sendToDatabase($user);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$dataSettings->bad_json_migrated = true;
|
||||
|
||||
$dataSettings->save();
|
||||
|
||||
Notification::make()
|
||||
->title('Data migration completed!')
|
||||
->body('Your history has been successfully migrated.')
|
||||
->success()
|
||||
->sendToDatabase($user);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ namespace App\Console;
|
||||
use App\Console\Commands\RunOoklaSpeedtest;
|
||||
use App\Console\Commands\SystemMaintenance;
|
||||
use App\Console\Commands\VersionChecker;
|
||||
use App\Models\Result;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Cron\CronExpression;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
@@ -19,6 +20,15 @@ class Kernel extends ConsoleKernel
|
||||
{
|
||||
$settings = new GeneralSettings();
|
||||
|
||||
/**
|
||||
* Checks if Result model records should be pruned.
|
||||
*/
|
||||
if ($settings->prune_results_older_than > 0) {
|
||||
$schedule->command('model:prune', [
|
||||
'--model' => [Result::class],
|
||||
])->daily();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform system maintenance weekly on Sunday morning,
|
||||
* start off the week nice and fresh.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Filament\Support\Contracts\HasLabel;
|
||||
|
||||
enum ResultStatus: string implements HasLabel
|
||||
{
|
||||
case Completed = 'completed'; // a speedtest that ran successfully.
|
||||
case Failed = 'failed'; // a speedtest that failed to run successfully.
|
||||
case Started = 'started'; // a speedtest that has been started by a worker but has not finish running.
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exports;
|
||||
|
||||
use Maatwebsite\Excel\Concerns\FromArray;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
|
||||
class ResultsSelectedBulkExport implements FromArray, WithHeadings
|
||||
{
|
||||
protected $results;
|
||||
|
||||
public function __construct(array $results)
|
||||
{
|
||||
$this->results = $results;
|
||||
}
|
||||
|
||||
public function array(): array
|
||||
{
|
||||
return $this->results;
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'id',
|
||||
'ping',
|
||||
'download',
|
||||
'upload',
|
||||
'server id',
|
||||
'server url',
|
||||
'server name',
|
||||
'result url',
|
||||
'scheduled',
|
||||
'successful',
|
||||
'data',
|
||||
'created at',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Exports;
|
||||
|
||||
use App\Models\Result;
|
||||
use Filament\Actions\Exports\Enums\ExportFormat;
|
||||
use Filament\Actions\Exports\ExportColumn;
|
||||
use Filament\Actions\Exports\Exporter;
|
||||
use Filament\Actions\Exports\Models\Export;
|
||||
|
||||
class ResultExporter extends Exporter
|
||||
{
|
||||
protected static ?string $model = Result::class;
|
||||
|
||||
public function getFormats(): array
|
||||
{
|
||||
return [
|
||||
ExportFormat::Csv,
|
||||
];
|
||||
}
|
||||
|
||||
public static function getColumns(): array
|
||||
{
|
||||
return [
|
||||
ExportColumn::make('id')
|
||||
->label('ID'),
|
||||
ExportColumn::make('ip_address')
|
||||
->label('IP address')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->ip_address;
|
||||
})
|
||||
->enabledByDefault(false),
|
||||
ExportColumn::make('service'),
|
||||
ExportColumn::make('server_id')
|
||||
->label('Server ID')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->server_id;
|
||||
})
|
||||
->enabledByDefault(false),
|
||||
ExportColumn::make('server_name')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->server_name;
|
||||
})
|
||||
->enabledByDefault(false),
|
||||
|
||||
ExportColumn::make('download')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->download_bits;
|
||||
}),
|
||||
ExportColumn::make('upload')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->download_bits;
|
||||
}),
|
||||
ExportColumn::make('ping'),
|
||||
ExportColumn::make('download_jitter')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->download_jitter;
|
||||
}),
|
||||
ExportColumn::make('upload_jitter')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->upload_jitter;
|
||||
}),
|
||||
ExportColumn::make('ping_jitter')
|
||||
->state(function (Result $record): ?string {
|
||||
return $record->ping_jitter;
|
||||
}),
|
||||
ExportColumn::make('comments')
|
||||
->enabledByDefault(false),
|
||||
// ExportColumn::make('status'), // TODO: enable status when upgrading to PHP v8.3: https://php.watch/versions/8.3/dynamic-class-const-enum-member-syntax-support
|
||||
ExportColumn::make('scheduled')
|
||||
->state(function (Result $record): string {
|
||||
return $record->scheduled ? 'Yes' : 'No';
|
||||
}),
|
||||
ExportColumn::make('created_at'),
|
||||
ExportColumn::make('updated_at')
|
||||
->enabledByDefault(false),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getCompletedNotificationBody(Export $export): string
|
||||
{
|
||||
$body = 'Your result export has completed and '.number_format($export->successful_rows).' '.str('row')->plural($export->successful_rows).' exported.';
|
||||
|
||||
if ($failedRowsCount = $export->getFailedRowsCount()) {
|
||||
$body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to export.';
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,14 @@ class GeneralPage extends SettingsPage
|
||||
->hint(new HtmlString('🔗<a href="https://crontab.cronhub.io/" target="_blank" rel="nofollow">Cron Generator</a>'))
|
||||
->nullable()
|
||||
->columnSpan(1),
|
||||
Forms\Components\TextInput::make('prune_results_older_than')
|
||||
->helperText('Set to zero to disable pruning.')
|
||||
->suffix('days')
|
||||
->numeric()
|
||||
->required()
|
||||
->minValue(0)
|
||||
->maxValue(9999)
|
||||
->columnSpan(1),
|
||||
Forms\Components\Select::make('speedtest_server')
|
||||
->label('Speedtest servers')
|
||||
->helperText('Leave empty to let the system pick the best server.')
|
||||
@@ -102,7 +110,7 @@ class GeneralPage extends SettingsPage
|
||||
->options(GetOoklaSpeedtestServers::run())
|
||||
->getSearchResultsUsing(fn (string $search): array => $this->getServerSearchOptions($search))
|
||||
->getOptionLabelsUsing(fn (array $values): array => $this->getServerLabels($values))
|
||||
->columnSpan('full'),
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->compact()
|
||||
->columns([
|
||||
|
||||
@@ -2,24 +2,28 @@
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Exports\ResultsSelectedBulkExport;
|
||||
use App\Actions\MigrateBadJsonResults;
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Filament\Exports\ResultExporter;
|
||||
use App\Filament\Resources\ResultResource\Pages;
|
||||
use App\Helpers\Number;
|
||||
use App\Helpers\TimeZoneHelper;
|
||||
use App\Models\Result;
|
||||
use App\Settings\DataMigrationSettings;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Carbon\Carbon;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Support\Enums\Alignment;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Actions\Action;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class ResultResource extends Resource
|
||||
{
|
||||
@@ -52,17 +56,6 @@ class ResultResource extends Resource
|
||||
$component->state(Carbon::parse($state)->format($settings->time_format ?? 'M j, Y G:i:s'));
|
||||
})
|
||||
->columnSpan(2),
|
||||
Forms\Components\TextInput::make('server_id')
|
||||
->label('Server ID'),
|
||||
Forms\Components\TextInput::make('server_name')
|
||||
->label('Server name')
|
||||
->columnSpan(2),
|
||||
Forms\Components\TextInput::make('server_host')
|
||||
->label('Server host')
|
||||
->columnSpan([
|
||||
'default' => 2,
|
||||
'md' => 3,
|
||||
]),
|
||||
Forms\Components\TextInput::make('download')
|
||||
->label('Download (Mbps)')
|
||||
->afterStateHydrated(function (TextInput $component, $state) {
|
||||
@@ -75,11 +68,31 @@ class ResultResource extends Resource
|
||||
}),
|
||||
Forms\Components\TextInput::make('ping')
|
||||
->label('Ping (Ms)'),
|
||||
Forms\Components\TextInput::make('data.download.latency.jitter')
|
||||
->label('Download Jitter (Ms)'),
|
||||
Forms\Components\TextInput::make('data.upload.latency.jitter')
|
||||
->label('Upload Jitter (Ms)'),
|
||||
Forms\Components\TextInput::make('data.ping.jitter')
|
||||
->label('Ping Jitter (Ms)'),
|
||||
Forms\Components\Textarea::make('data.message')
|
||||
->label('Error Message')
|
||||
->hint(new HtmlString('🔗<a href="https://docs.speedtest-tracker.dev/help/error-messages" target="_blank" rel="nofollow">Error Messages</a>'))
|
||||
->hidden(fn (Result $record): bool => $record->status !== ResultStatus::Failed)
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columnSpan(2),
|
||||
Forms\Components\Section::make()
|
||||
->schema([
|
||||
Forms\Components\Checkbox::make('successful'),
|
||||
Forms\Components\Placeholder::make('service')
|
||||
->content(fn (Result $result): string => $result->service),
|
||||
Forms\Components\Placeholder::make('server_name')
|
||||
->content(fn (Result $result): ?string => $result->server_name),
|
||||
Forms\Components\Placeholder::make('server_id')
|
||||
->label('Server ID')
|
||||
->content(fn (Result $result): ?string => $result->server_id),
|
||||
Forms\Components\Placeholder::make('server_host')
|
||||
->label('Server ID')
|
||||
->content(fn (Result $result): ?string => $result->server_id),
|
||||
Forms\Components\Checkbox::make('scheduled'),
|
||||
])
|
||||
->columns(1)
|
||||
@@ -88,65 +101,95 @@ class ResultResource extends Resource
|
||||
'md' => 1,
|
||||
]),
|
||||
]),
|
||||
Forms\Components\Textarea::make('data')
|
||||
->rows(10)
|
||||
->columnSpan(2),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
$dataSettings = new DataMigrationSettings();
|
||||
|
||||
$settings = new GeneralSettings();
|
||||
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('id')
|
||||
Tables\Columns\TextColumn::make('id')
|
||||
->label('ID')
|
||||
->sortable(),
|
||||
TextColumn::make('server')
|
||||
->getStateUsing(fn (Result $record): ?string => ! blank($record->server_id) ? $record->server_id.' ('.$record->server_name.')' : null)
|
||||
Tables\Columns\TextColumn::make('ip_address')
|
||||
->label('IP address')
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('service')
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('server_id')
|
||||
->label('Server ID')
|
||||
->toggleable()
|
||||
->sortable(),
|
||||
IconColumn::make('successful')
|
||||
Tables\Columns\TextColumn::make('server_name')
|
||||
->toggleable()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('download')
|
||||
->getStateUsing(fn (Result $record): ?string => ! blank($record->download) ? Number::fileSizeBits(bits: $record->download, precision: 2, perSecond: true) : null)
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('upload')
|
||||
->getStateUsing(fn (Result $record): ?string => ! blank($record->upload) ? Number::fileSizeBits(bits: $record->upload, precision: 2, perSecond: true) : null)
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('ping')
|
||||
->toggleable()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('download_jitter')
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('upload_jitter')
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('ping_jitter')
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('status')
|
||||
->toggleable()
|
||||
->sortable(),
|
||||
Tables\Columns\IconColumn::make('scheduled')
|
||||
->boolean()
|
||||
->toggleable(),
|
||||
IconColumn::make('scheduled')
|
||||
->boolean()
|
||||
->toggleable(),
|
||||
TextColumn::make('download')
|
||||
->label('Download (Mbps)')
|
||||
->getStateUsing(fn (Result $record): ?string => ! blank($record->download) ? toBits(convertSize($record->download), 2) : null)
|
||||
->sortable(),
|
||||
TextColumn::make('upload')
|
||||
->label('Upload (Mbps)')
|
||||
->getStateUsing(fn (Result $record): ?string => ! blank($record->upload) ? toBits(convertSize($record->upload), 2) : null)
|
||||
->sortable(),
|
||||
TextColumn::make('ping')
|
||||
->label('Ping (Ms)')
|
||||
->toggleable()
|
||||
->sortable(),
|
||||
TextColumn::make('download_jitter')
|
||||
->getStateUsing(fn (Result $record): ?string => json_decode($record->data, true)['download']['latency']['jitter'] ?? null)
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
TextColumn::make('upload_jitter')
|
||||
->getStateUsing(fn (Result $record): ?string => json_decode($record->data, true)['upload']['latency']['jitter'] ?? null)
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
TextColumn::make('ping_jitter')
|
||||
->getStateUsing(fn (Result $record): ?string => json_decode($record->data, true)['ping']['jitter'] ?? null)
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->label('Created')
|
||||
->alignment(Alignment::Center),
|
||||
Tables\Columns\TextColumn::make('created_at')
|
||||
->dateTime($settings->time_format ?? 'M j, Y G:i:s')
|
||||
->timezone(TimeZoneHelper::displayTimeZone($settings))
|
||||
->sortable(),
|
||||
->toggleable()
|
||||
->sortable()
|
||||
->alignment(Alignment::End),
|
||||
Tables\Columns\TextColumn::make('updated_at')
|
||||
->dateTime($settings->time_format ?? 'M j, Y G:i:s')
|
||||
->timezone(TimeZoneHelper::displayTimeZone($settings))
|
||||
->toggleable()
|
||||
->toggledHiddenByDefault()
|
||||
->sortable()
|
||||
->alignment(Alignment::End),
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\SelectFilter::make('ip_address')
|
||||
->label('IP address')
|
||||
->multiple()
|
||||
->options(function (): array {
|
||||
return Result::query()
|
||||
->select('data->interface->externalIp AS public_ip_address')
|
||||
->where('status', '=', ResultStatus::Completed)
|
||||
->distinct()
|
||||
->get()
|
||||
->mapWithKeys(function (Result $item, int $key) {
|
||||
return [$item['public_ip_address'] => $item['public_ip_address']];
|
||||
})
|
||||
->toArray();
|
||||
})
|
||||
->attribute('data->interface->externalIp'),
|
||||
Tables\Filters\TernaryFilter::make('scheduled')
|
||||
->placeholder('-')
|
||||
->trueLabel('Only scheduled speedtests')
|
||||
@@ -156,23 +199,17 @@ class ResultResource extends Resource
|
||||
false: fn (Builder $query) => $query->where('scheduled', false),
|
||||
blank: fn (Builder $query) => $query,
|
||||
),
|
||||
Tables\Filters\TernaryFilter::make('successful')
|
||||
->placeholder('-')
|
||||
->trueLabel('Only successful speedtests')
|
||||
->falseLabel('Only failed speedtests')
|
||||
->queries(
|
||||
true: fn (Builder $query) => $query->where('successful', true),
|
||||
false: fn (Builder $query) => $query->where('successful', false),
|
||||
blank: fn (Builder $query) => $query,
|
||||
),
|
||||
Tables\Filters\SelectFilter::make('status')
|
||||
->multiple()
|
||||
->options(ResultStatus::class),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ActionGroup::make([
|
||||
Action::make('view result')
|
||||
->label('View on Speedtest.net')
|
||||
->icon('heroicon-o-link')
|
||||
->url(fn (Result $record): ?string => $record?->url)
|
||||
->hidden(fn (Result $record): bool => ! $record->is_successful)
|
||||
->url(fn (Result $record): ?string => $record->result_url)
|
||||
->hidden(fn (Result $record): bool => $record->status !== ResultStatus::Completed)
|
||||
->openUrlInNewTab(),
|
||||
Tables\Actions\ViewAction::make(),
|
||||
Tables\Actions\Action::make('updateComments')
|
||||
@@ -195,25 +232,31 @@ class ResultResource extends Resource
|
||||
]),
|
||||
])
|
||||
->bulkActions([
|
||||
Tables\Actions\BulkAction::make('export')
|
||||
->label('Export selected')
|
||||
->icon('heroicon-o-arrow-down-tray')
|
||||
->hidden(fn (): bool => ! auth()->user()->is_admin)
|
||||
->action(function (Collection $records) {
|
||||
$export = new ResultsSelectedBulkExport($records->toArray());
|
||||
|
||||
return Excel::download($export, 'results_'.now()->timestamp.'.csv', \Maatwebsite\Excel\Excel::CSV);
|
||||
}),
|
||||
Tables\Actions\DeleteBulkAction::make(),
|
||||
])
|
||||
->defaultSort('created_at', 'desc');
|
||||
}
|
||||
->headerActions([
|
||||
Tables\Actions\ExportAction::make()
|
||||
->exporter(ResultExporter::class)
|
||||
->fileName(fn (): string => 'results-'.now()->timestamp),
|
||||
Tables\Actions\Action::make('migrate')
|
||||
->action(function (): void {
|
||||
Notification::make()
|
||||
->title('Starting data migration...')
|
||||
->body('This can take a little bit depending how much data you have.')
|
||||
->warning()
|
||||
->sendToDatabase(Auth::user());
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
MigrateBadJsonResults::dispatch(Auth::user());
|
||||
})
|
||||
->hidden($dataSettings->bad_json_migrated)
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Migrate History')
|
||||
->modalDescription(new HtmlString('<p>v0.16.0 archived the old <code>"results"</code> table, to migrate your history click the button below.</p><p>For more information read the <a href="#" target="_blank" rel="nofollow" class="underline">docs</a>.</p>'))
|
||||
->modalSubmitActionLabel('Yes, migrate it'),
|
||||
])
|
||||
->defaultSort('created_at', 'desc')
|
||||
->paginated([5, 15, 25, 50, 100])
|
||||
->defaultPaginationPageOption(15);
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Helpers\TimeZoneHelper;
|
||||
use App\Models\Result;
|
||||
use App\Settings\GeneralSettings;
|
||||
@@ -37,6 +38,7 @@ class RecentJitterChartWidget extends ChartWidget
|
||||
|
||||
$results = Result::query()
|
||||
->select(['data', 'created_at'])
|
||||
->where('status', '=', ResultStatus::Completed)
|
||||
->when($this->filter == '24h', function ($query) {
|
||||
$query->where('created_at', '>=', now()->subDay());
|
||||
})
|
||||
@@ -52,7 +54,7 @@ class RecentJitterChartWidget extends ChartWidget
|
||||
'datasets' => [
|
||||
[
|
||||
'label' => 'Download (ms)',
|
||||
'data' => $results->map(fn ($item) => $item->getJitterData()['download'] ? number_format($item->getJitterData()['download'], 2) : 0),
|
||||
'data' => $results->map(fn ($item) => $item->download_jitter ? number_format($item->download_jitter, 2) : 0),
|
||||
'borderColor' => '#0ea5e9',
|
||||
'backgroundColor' => '#0ea5e9',
|
||||
'fill' => false,
|
||||
@@ -61,7 +63,7 @@ class RecentJitterChartWidget extends ChartWidget
|
||||
],
|
||||
[
|
||||
'label' => 'Upload (ms)',
|
||||
'data' => $results->map(fn ($item) => $item->getJitterData()['upload'] ? number_format($item->getJitterData()['upload'], 2) : 0),
|
||||
'data' => $results->map(fn ($item) => $item->upload_jitter ? number_format($item->upload_jitter, 2) : 0),
|
||||
'borderColor' => '#8b5cf6',
|
||||
'backgroundColor' => '#8b5cf6',
|
||||
'fill' => false,
|
||||
@@ -70,7 +72,7 @@ class RecentJitterChartWidget extends ChartWidget
|
||||
],
|
||||
[
|
||||
'label' => 'Ping (ms)',
|
||||
'data' => $results->map(fn ($item) => $item->getJitterData()['ping'] ? number_format($item->getJitterData()['ping'], 2) : 0),
|
||||
'data' => $results->map(fn ($item) => $item->ping_jitter ? number_format($item->ping_jitter, 2) : 0),
|
||||
'borderColor' => '#10b981',
|
||||
'backgroundColor' => '#10b981',
|
||||
'fill' => false,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Helpers\TimeZoneHelper;
|
||||
use App\Models\Result;
|
||||
use App\Settings\GeneralSettings;
|
||||
@@ -37,6 +38,7 @@ class RecentPingChartWidget extends ChartWidget
|
||||
|
||||
$results = Result::query()
|
||||
->select(['ping', 'created_at'])
|
||||
->where('status', '=', ResultStatus::Completed)
|
||||
->when($this->filter == '24h', function ($query) {
|
||||
$query->where('created_at', '>=', now()->subDay());
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Helpers\TimeZoneHelper;
|
||||
use App\Models\Result;
|
||||
use App\Settings\GeneralSettings;
|
||||
@@ -37,6 +38,7 @@ class RecentSpeedChartWidget extends ChartWidget
|
||||
|
||||
$results = Result::query()
|
||||
->select(['id', 'download', 'upload', 'created_at'])
|
||||
->where('status', '=', ResultStatus::Completed)
|
||||
->when($this->filter == '24h', function ($query) {
|
||||
$query->where('created_at', '>=', now()->subDay());
|
||||
})
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Helpers\Number;
|
||||
use App\Models\Result;
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
@@ -18,10 +20,12 @@ class StatsOverviewWidget extends BaseWidget
|
||||
protected function getCards(): array
|
||||
{
|
||||
$this->result = Result::query()
|
||||
->select(['id', 'ping', 'download', 'upload', 'status', 'created_at'])
|
||||
->where('status', '=', ResultStatus::Completed)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if (blank($this->result) || ! $this->result->successful) {
|
||||
if (blank($this->result)) {
|
||||
return [
|
||||
Stat::make('Latest download', '-')
|
||||
->icon('heroicon-o-arrow-down-tray'),
|
||||
@@ -33,17 +37,19 @@ class StatsOverviewWidget extends BaseWidget
|
||||
}
|
||||
|
||||
$previous = Result::query()
|
||||
->select(['id', 'ping', 'download', 'upload', 'status', 'created_at'])
|
||||
->where('id', '<', $this->result->id)
|
||||
->where('status', '=', ResultStatus::Completed)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if (! $previous || ! $previous->successful) {
|
||||
if (! $previous) {
|
||||
return [
|
||||
Stat::make('Latest download', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->download), 2).' (Mbps)' : 'n/a')
|
||||
Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->download_bits, precision: 2, perSecond: true) : 'n/a')
|
||||
->icon('heroicon-o-arrow-down-tray'),
|
||||
Stat::make('Latest upload', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->upload), 2).' (Mbps)' : 'n/a')
|
||||
Stat::make('Latest upload', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->upload_bits, precision: 2, perSecond: true) : 'n/a')
|
||||
->icon('heroicon-o-arrow-up-tray'),
|
||||
Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' (ms)' : 'n/a')
|
||||
Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' Ms' : 'n/a')
|
||||
->icon('heroicon-o-clock'),
|
||||
];
|
||||
}
|
||||
@@ -53,17 +59,17 @@ class StatsOverviewWidget extends BaseWidget
|
||||
$pingChange = percentChange($this->result->ping, $previous->ping, 2);
|
||||
|
||||
return [
|
||||
Stat::make('Latest download', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->download), 2).' (Mbps)' : 'n/a')
|
||||
Stat::make('Latest download', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->download_bits, precision: 2, perSecond: true) : 'n/a')
|
||||
->icon('heroicon-o-arrow-down-tray')
|
||||
->description($downloadChange > 0 ? $downloadChange.'% faster' : abs($downloadChange).'% slower')
|
||||
->descriptionIcon($downloadChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down')
|
||||
->color($downloadChange > 0 ? 'success' : 'danger'),
|
||||
Stat::make('Latest upload', fn (): string => ! blank($this->result) ? toBits(convertSize($this->result->upload), 2).' (Mbps)' : 'n/a')
|
||||
Stat::make('Latest upload', fn (): string => ! blank($this->result) ? Number::fileSizeBits(bits: $this->result->upload_bits, precision: 2, perSecond: true) : 'n/a')
|
||||
->icon('heroicon-o-arrow-up-tray')
|
||||
->description($uploadChange > 0 ? $uploadChange.'% faster' : abs($uploadChange).'% slower')
|
||||
->descriptionIcon($uploadChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down')
|
||||
->color($uploadChange > 0 ? 'success' : 'danger'),
|
||||
Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' (ms)' : 'n/a')
|
||||
Stat::make('Latest ping', fn (): string => ! blank($this->result) ? number_format($this->result->ping, 2).' Ms' : 'n/a')
|
||||
->icon('heroicon-o-clock')
|
||||
->description($pingChange > 0 ? $pingChange.'% slower' : abs($pingChange).'% faster')
|
||||
->descriptionIcon($pingChange > 0 ? 'heroicon-m-arrow-trending-up' : 'heroicon-m-arrow-trending-down')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\API\Speedtest;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Result;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -14,6 +15,7 @@ class GetLatestController extends Controller
|
||||
public function __invoke(): JsonResponse
|
||||
{
|
||||
$latest = Result::query()
|
||||
->whereIn('status', [ResultStatus::Completed, ResultStatus::Failed])
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
@@ -28,16 +30,16 @@ class GetLatestController extends Controller
|
||||
'data' => [
|
||||
'id' => $latest->id,
|
||||
'ping' => $latest->ping,
|
||||
'download' => ! blank($latest->download) ? toBits(convertSize($latest->download)) : null,
|
||||
'upload' => ! blank($latest->upload) ? toBits(convertSize($latest->upload)) : null,
|
||||
'download' => $latest->download_bits,
|
||||
'upload' => $latest->upload_bits,
|
||||
'server_id' => $latest->server_id,
|
||||
'server_host' => $latest->server_host,
|
||||
'server_name' => $latest->server_name,
|
||||
'url' => $latest->url,
|
||||
'url' => $latest->result_url,
|
||||
'scheduled' => $latest->scheduled,
|
||||
'failed' => ! $latest->successful,
|
||||
'failed' => $latest->status === ResultStatus::Failed,
|
||||
'created_at' => $latest->created_at->toISOString(true),
|
||||
'updated_at' => $latest->created_at->toISOString(true), // faking updated at to match legacy api payload
|
||||
'updated_at' => $latest->updated_at->toISOString(true),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Models\Result;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -20,7 +21,8 @@ class HomeController extends Controller
|
||||
}
|
||||
|
||||
$latestResult = Result::query()
|
||||
->select(['id', 'ping', 'download', 'upload', 'successful', 'created_at'])
|
||||
->select(['id', 'ping', 'download', 'upload', 'status', 'created_at'])
|
||||
->where('status', '=', ResultStatus::Completed)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
|
||||
+12
-12
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Models\Result;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||
use Symfony\Component\Process\Process;
|
||||
@@ -50,28 +52,26 @@ class ExecSpeedtest implements ShouldQueue
|
||||
$message = collect(array_filter($messages, 'json_validate'))->last();
|
||||
|
||||
Result::create([
|
||||
'service' => 'ookla',
|
||||
'data' => json_decode($message, true),
|
||||
'status' => ResultStatus::Failed,
|
||||
'scheduled' => $this->scheduled,
|
||||
'successful' => false,
|
||||
'data' => $message,
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$output = $process->getOutput();
|
||||
$results = json_decode($output, true);
|
||||
$results = json_decode($process->getOutput(), true);
|
||||
|
||||
Result::create([
|
||||
'ping' => $results['ping']['latency'],
|
||||
'download' => $results['download']['bandwidth'],
|
||||
'upload' => $results['upload']['bandwidth'],
|
||||
'server_id' => $results['server']['id'],
|
||||
'server_name' => $results['server']['name'],
|
||||
'server_host' => $results['server']['host'].':'.$results['server']['port'],
|
||||
'url' => $results['result']['url'],
|
||||
'service' => 'ookla',
|
||||
'ping' => Arr::get($results, 'ping.latency'),
|
||||
'download' => Arr::get($results, 'download.bandwidth'),
|
||||
'upload' => Arr::get($results, 'upload.bandwidth'),
|
||||
'data' => $results,
|
||||
'status' => ResultStatus::Completed,
|
||||
'scheduled' => $this->scheduled,
|
||||
'data' => $output,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
|
||||
+134
-43
@@ -2,41 +2,26 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ResultStatus;
|
||||
use App\Events\ResultCreated;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Prunable;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Result extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, Prunable;
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'ping',
|
||||
'download',
|
||||
'upload',
|
||||
'server_id',
|
||||
'server_host',
|
||||
'server_name',
|
||||
'url',
|
||||
'comments',
|
||||
'scheduled',
|
||||
'successful',
|
||||
'data',
|
||||
];
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
@@ -44,10 +29,9 @@ class Result extends Model
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'scheduled' => 'boolean',
|
||||
'successful' => 'boolean',
|
||||
'data' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'status' => ResultStatus::class,
|
||||
'scheduled' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -76,36 +60,33 @@ class Result extends Model
|
||||
*/
|
||||
public function formatForInfluxDB2()
|
||||
{
|
||||
$data = json_decode($this->data, true);
|
||||
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'ping' => $this?->ping,
|
||||
'download' => $this?->download,
|
||||
'upload' => $this?->upload,
|
||||
'download_bits' => $this->download ? $this->download * 8 : null,
|
||||
'upload_bits' => $this->upload ? $this->upload * 8 : null,
|
||||
'ping_jitter' => Arr::get($data, 'ping.jitter'),
|
||||
'download_jitter' => Arr::get($data, 'download.latency.jitter'),
|
||||
'upload_jitter' => Arr::get($data, 'upload.latency.jitter'),
|
||||
'download_bits' => $this->download_bits,
|
||||
'upload_bits' => $this->upload_bits,
|
||||
'ping_jitter' => $this->ping_jitter,
|
||||
'download_jitter' => $this->download_jitter,
|
||||
'upload_jitter' => $this->upload_jitter,
|
||||
'server_id' => $this?->server_id,
|
||||
'server_host' => $this?->server_host,
|
||||
'server_name' => $this?->server_name,
|
||||
'scheduled' => $this->scheduled,
|
||||
'successful' => $this->successful,
|
||||
'packet_loss' => (float) Arr::get($data, 'packetLoss', 0),
|
||||
'successful' => $this->status === ResultStatus::Completed,
|
||||
'packet_loss' => (float) $this->packet_loss,
|
||||
];
|
||||
}
|
||||
|
||||
public function getJitterData(): array
|
||||
/**
|
||||
* Get the prunable model query.
|
||||
*/
|
||||
public function prunable(): Builder
|
||||
{
|
||||
$data = json_decode($this->data, true);
|
||||
$settings = new GeneralSettings();
|
||||
|
||||
return [
|
||||
'download' => Arr::get($data, 'download.latency.jitter'),
|
||||
'upload' => Arr::get($data, 'upload.latency.jitter'),
|
||||
'ping' => Arr::get($data, 'ping.jitter'),
|
||||
];
|
||||
return static::where('created_at', '<=', now()->subDays($settings->prune_results_older_than));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,21 +95,131 @@ class Result extends Model
|
||||
protected function downloadBits(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (mixed $value): ?string => ! blank($this->download) && is_numeric($this->download)
|
||||
get: fn (): ?string => ! blank($this->download) && is_numeric($this->download)
|
||||
? number_format(num: $this->download * 8, decimals: 0, thousands_separator: '')
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's download jitter in milliseconds.
|
||||
*/
|
||||
protected function downloadJitter(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'download.latency.jitter'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's download jitter in milliseconds.
|
||||
*/
|
||||
protected function errorMessage(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'message', ''),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's external ip address (yours).
|
||||
*/
|
||||
protected function ipAddress(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'interface.externalIp'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's isp tied to the external (yours) ip address.
|
||||
*/
|
||||
protected function isp(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'isp'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's packet loss as a percentage.
|
||||
*/
|
||||
protected function packetLoss(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'packetLoss'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's ping jitter in milliseconds.
|
||||
*/
|
||||
protected function pingJitter(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'ping.jitter'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's server ID.
|
||||
*/
|
||||
protected function resultUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'result.url'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's server host.
|
||||
*/
|
||||
protected function serverHost(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'server.host'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's server ID.
|
||||
*/
|
||||
protected function serverId(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'server.id'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's server name.
|
||||
*/
|
||||
protected function serverName(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'server.name'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's upload in bits.
|
||||
*/
|
||||
protected function uploadBits(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (mixed $value): ?string => ! blank($this->upload) && is_numeric($this->upload)
|
||||
get: fn (): ?string => ! blank($this->upload) && is_numeric($this->upload)
|
||||
? number_format(num: $this->upload * 8, decimals: 0, thousands_separator: '')
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the result's upload jitter in milliseconds.
|
||||
*/
|
||||
protected function uploadJitter(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Arr::get($this->data, 'upload.latency.jitter'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Settings;
|
||||
|
||||
use Spatie\LaravelSettings\Settings;
|
||||
|
||||
class DataMigrationSettings extends Settings
|
||||
{
|
||||
public bool $bad_json_migrated;
|
||||
|
||||
public static function group(): string
|
||||
{
|
||||
return 'data_migration';
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ class GeneralSettings extends Settings
|
||||
{
|
||||
public bool $auth_enabled;
|
||||
|
||||
public int $prune_results_older_than;
|
||||
|
||||
public ?string $speedtest_schedule;
|
||||
|
||||
/** @var string[] */
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
"laravel/tinker": "^2.9.0",
|
||||
"livewire/livewire": "^3.4.4",
|
||||
"lorisleiva/laravel-actions": "^2.7.3",
|
||||
"maatwebsite/excel": "^3.1.53",
|
||||
"maennchen/zipstream-php": "^2.4",
|
||||
"spatie/laravel-settings": "^2.8.3",
|
||||
"spatie/laravel-webhook-server": "^3.8.1",
|
||||
|
||||
Generated
+1
-436
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "708b922946903f72ebba765fd96d4e23",
|
||||
"content-hash": "881afa5632aa340d082788ac94701e64",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@@ -525,87 +525,6 @@
|
||||
],
|
||||
"time": "2023-12-20T15:40:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
"version": "3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/semver.git",
|
||||
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32",
|
||||
"reference": "35e8d0af4486141bc745f23a29cc2091eb624a32",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.2 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"symfony/phpunit-bridge": "^4.2 || ^5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\Semver\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nils Adermann",
|
||||
"email": "naderman@naderman.de",
|
||||
"homepage": "http://www.naderman.de"
|
||||
},
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http://seld.be"
|
||||
},
|
||||
{
|
||||
"name": "Rob Bast",
|
||||
"email": "rob.bast@gmail.com",
|
||||
"homepage": "http://robbast.nl"
|
||||
}
|
||||
],
|
||||
"description": "Semver library that offers utilities, version constraint parsing and validation.",
|
||||
"keywords": [
|
||||
"semantic",
|
||||
"semver",
|
||||
"validation",
|
||||
"versioning"
|
||||
],
|
||||
"support": {
|
||||
"irc": "ircs://irc.libera.chat:6697/composer",
|
||||
"issues": "https://github.com/composer/semver/issues",
|
||||
"source": "https://github.com/composer/semver/tree/3.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/composer",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-31T09:50:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "danharrin/date-format-converter",
|
||||
"version": "v0.3.0",
|
||||
@@ -1426,67 +1345,6 @@
|
||||
],
|
||||
"time": "2023-10-06T06:47:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"version": "v4.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||
"reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c",
|
||||
"reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cerdic/css-tidy": "^1.7 || ^2.0",
|
||||
"simpletest/simpletest": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
|
||||
"ext-bcmath": "Used for unit conversion and imagecrash protection",
|
||||
"ext-iconv": "Converts text to and from non-UTF-8 encodings",
|
||||
"ext-tidy": "Used for pretty-printing HTML"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"library/HTMLPurifier.composer.php"
|
||||
],
|
||||
"psr-0": {
|
||||
"HTMLPurifier": "library/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/library/HTMLPurifier/Language/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Edward Z. Yang",
|
||||
"email": "admin@htmlpurifier.org",
|
||||
"homepage": "http://ezyang.com"
|
||||
}
|
||||
],
|
||||
"description": "Standards compliant HTML filter written in PHP",
|
||||
"homepage": "http://htmlpurifier.org/",
|
||||
"keywords": [
|
||||
"html"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ezyang/htmlpurifier/issues",
|
||||
"source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0"
|
||||
},
|
||||
"time": "2023-11-17T15:01:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "filament/actions",
|
||||
"version": "v3.2.34",
|
||||
@@ -4028,87 +3886,6 @@
|
||||
],
|
||||
"time": "2023-02-05T15:03:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maatwebsite/excel",
|
||||
"version": "3.1.53",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SpartnerNL/Laravel-Excel.git",
|
||||
"reference": "f5175b19389f51bf489a1558eb0efc0a59ec4b67"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/f5175b19389f51bf489a1558eb0efc0a59ec4b67",
|
||||
"reference": "f5175b19389f51bf489a1558eb0efc0a59ec4b67",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/semver": "^3.3",
|
||||
"ext-json": "*",
|
||||
"illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0",
|
||||
"php": "^7.0||^8.0",
|
||||
"phpoffice/phpspreadsheet": "^1.18",
|
||||
"psr/simple-cache": "^1.0||^2.0||^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/scout": "^7.0||^8.0||^9.0||^10.0",
|
||||
"orchestra/testbench": "^6.0||^7.0||^8.0",
|
||||
"predis/predis": "^1.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Maatwebsite\\Excel\\ExcelServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Excel": "Maatwebsite\\Excel\\Facades\\Excel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Maatwebsite\\Excel\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Patrick Brouwers",
|
||||
"email": "patrick@spartner.nl"
|
||||
}
|
||||
],
|
||||
"description": "Supercharged Excel exports and imports in Laravel",
|
||||
"keywords": [
|
||||
"PHPExcel",
|
||||
"batch",
|
||||
"csv",
|
||||
"excel",
|
||||
"export",
|
||||
"import",
|
||||
"laravel",
|
||||
"php",
|
||||
"phpspreadsheet"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/SpartnerNL/Laravel-Excel/issues",
|
||||
"source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.53"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://laravel-excel.com/commercial-support",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/patrickbrouwers",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-02-05T08:53:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maennchen/zipstream-php",
|
||||
"version": "2.4.0",
|
||||
@@ -4187,113 +3964,6 @@
|
||||
],
|
||||
"time": "2022-12-08T12:29:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "markbaker/complex",
|
||||
"version": "3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPComplex.git",
|
||||
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||
"reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Complex\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@lange.demon.co.uk"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with complex numbers",
|
||||
"homepage": "https://github.com/MarkBaker/PHPComplex",
|
||||
"keywords": [
|
||||
"complex",
|
||||
"mathematics"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPComplex/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
|
||||
},
|
||||
"time": "2022-12-06T16:21:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "markbaker/matrix",
|
||||
"version": "3.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/MarkBaker/PHPMatrix.git",
|
||||
"reference": "728434227fe21be27ff6d86621a1b13107a2562c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
|
||||
"reference": "728434227fe21be27ff6d86621a1b13107a2562c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpdocumentor/phpdocumentor": "2.*",
|
||||
"phploc/phploc": "^4.0",
|
||||
"phpmd/phpmd": "2.*",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
|
||||
"sebastian/phpcpd": "^4.0",
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Matrix\\": "classes/src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"email": "mark@demon-angel.eu"
|
||||
}
|
||||
],
|
||||
"description": "PHP Class for working with matrices",
|
||||
"homepage": "https://github.com/MarkBaker/PHPMatrix",
|
||||
"keywords": [
|
||||
"mathematics",
|
||||
"matrix",
|
||||
"vector"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MarkBaker/PHPMatrix/issues",
|
||||
"source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
|
||||
},
|
||||
"time": "2022-12-02T22:17:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "masterminds/html5",
|
||||
"version": "2.8.1",
|
||||
@@ -5453,111 +5123,6 @@
|
||||
},
|
||||
"time": "2024-01-11T11:49:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoffice/phpspreadsheet",
|
||||
"version": "1.29.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
|
||||
"reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fde2ccf55eaef7e86021ff1acce26479160a0fa0",
|
||||
"reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-simplexml": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": "*",
|
||||
"ezyang/htmlpurifier": "^4.15",
|
||||
"maennchen/zipstream-php": "^2.1 || ^3.0",
|
||||
"markbaker/complex": "^3.0",
|
||||
"markbaker/matrix": "^3.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-main",
|
||||
"dompdf/dompdf": "^1.0 || ^2.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"mitoteam/jpgraph": "^10.3",
|
||||
"mpdf/mpdf": "^8.1.1",
|
||||
"phpcompatibility/php-compatibility": "^9.3",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"phpstan/phpstan-phpunit": "^1.0",
|
||||
"phpunit/phpunit": "^8.5 || ^9.0 || ^10.0",
|
||||
"squizlabs/php_codesniffer": "^3.7",
|
||||
"tecnickcom/tcpdf": "^6.5"
|
||||
},
|
||||
"suggest": {
|
||||
"dompdf/dompdf": "Option for rendering PDF with PDF Writer",
|
||||
"ext-intl": "PHP Internationalization Functions",
|
||||
"mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
|
||||
"mpdf/mpdf": "Option for rendering PDF with PDF Writer",
|
||||
"tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Maarten Balliauw",
|
||||
"homepage": "https://blog.maartenballiauw.be"
|
||||
},
|
||||
{
|
||||
"name": "Mark Baker",
|
||||
"homepage": "https://markbakeruk.net"
|
||||
},
|
||||
{
|
||||
"name": "Franck Lefevre",
|
||||
"homepage": "https://rootslabs.net"
|
||||
},
|
||||
{
|
||||
"name": "Erik Tilt"
|
||||
},
|
||||
{
|
||||
"name": "Adrien Crivelli"
|
||||
}
|
||||
],
|
||||
"description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
|
||||
"homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
|
||||
"keywords": [
|
||||
"OpenXML",
|
||||
"excel",
|
||||
"gnumeric",
|
||||
"ods",
|
||||
"php",
|
||||
"spreadsheet",
|
||||
"xls",
|
||||
"xlsx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
|
||||
"source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.0"
|
||||
},
|
||||
"time": "2023-06-14T22:48:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.2",
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use App\Settings\DataMigrationSettings;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Filament\Notifications\Actions\Action;
|
||||
use Filament\Notifications\Notification;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasTable('results')) {
|
||||
/**
|
||||
* Rename the existing table so that a backup copy exists.
|
||||
*/
|
||||
Schema::rename('results', 'results_bad_json');
|
||||
}
|
||||
|
||||
if (! Schema::hasTable('results')) {
|
||||
/**
|
||||
* Create a new results table based on a new DDL schema.
|
||||
*/
|
||||
Schema::create('results', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('service')->default('ookla');
|
||||
$table->float('ping', 8, 3)->nullable();
|
||||
$table->unsignedBigInteger('download')->nullable();
|
||||
$table->unsignedBigInteger('upload')->nullable();
|
||||
$table->text('comments')->nullable();
|
||||
$table->json('data')->nullable();
|
||||
$table->string('status');
|
||||
$table->boolean('scheduled')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't disable the schedule or send a notification if there are no records.
|
||||
*/
|
||||
if (! DB::table('results_bad_json')->count()) {
|
||||
$dataSettings = new DataMigrationSettings();
|
||||
|
||||
$dataSettings->bad_json_migrated = true;
|
||||
|
||||
$dataSettings->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = new GeneralSettings();
|
||||
|
||||
$settings->speedtest_schedule = '';
|
||||
|
||||
$settings->save();
|
||||
|
||||
$admins = User::select(['id', 'name', 'email', 'role'])
|
||||
->where('role', 'admin')
|
||||
->get();
|
||||
|
||||
foreach ($admins as $user) {
|
||||
Notification::make()
|
||||
->title('Breaking change, user action required!')
|
||||
->body('v0.16.0 includes a breaking change to resolve a data quality issue. Read the release notes regarding the data migration.')
|
||||
->danger()
|
||||
->actions([
|
||||
Action::make('Release notes')
|
||||
->button()
|
||||
->url('https://github.com/alexjustesen/speedtest-tracker/releases/tag/v0.16.0')
|
||||
->openUrlInNewTab(),
|
||||
])
|
||||
->sendToDatabase($user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::drop('results');
|
||||
|
||||
if (! Schema::hasTable('results')) {
|
||||
Schema::rename('results_bad_json', 'results');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('job_batches', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->string('name');
|
||||
$table->integer('total_jobs');
|
||||
$table->integer('pending_jobs');
|
||||
$table->integer('failed_jobs');
|
||||
$table->longText('failed_job_ids');
|
||||
$table->mediumText('options')->nullable();
|
||||
$table->integer('cancelled_at')->nullable();
|
||||
$table->integer('created_at');
|
||||
$table->integer('finished_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('job_batches');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class() extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('imports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->timestamp('completed_at')->nullable();
|
||||
$table->string('file_name');
|
||||
$table->string('file_path');
|
||||
$table->string('importer');
|
||||
$table->unsignedInteger('processed_rows')->default(0);
|
||||
$table->unsignedInteger('total_rows');
|
||||
$table->unsignedInteger('successful_rows')->default(0);
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('imports');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class() extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('exports', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->timestamp('completed_at')->nullable();
|
||||
$table->string('file_disk');
|
||||
$table->string('file_name')->nullable();
|
||||
$table->string('exporter');
|
||||
$table->unsignedInteger('processed_rows')->default(0);
|
||||
$table->unsignedInteger('total_rows');
|
||||
$table->unsignedInteger('successful_rows')->default(0);
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('exports');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class() extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('failed_import_rows', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->json('data');
|
||||
$table->foreignId('import_id')->constrained()->cascadeOnDelete();
|
||||
$table->text('validation_error')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('failed_import_rows');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Spatie\LaravelSettings\Migrations\SettingsMigration;
|
||||
|
||||
return new class extends SettingsMigration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$this->migrator->add('data_migration.bad_json_migrated', false);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Spatie\LaravelSettings\Migrations\SettingsMigration;
|
||||
|
||||
return new class extends SettingsMigration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$this->migrator->add('general.prune_results_older_than', 0);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user