Dev Release v0.16.0 (#1143)

This commit is contained in:
Alex Justesen
2024-02-19 11:22:51 -05:00
committed by GitHub
parent e56028f634
commit 18602f571f
29 changed files with 819 additions and 1009 deletions
+11 -66
View File
@@ -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,
+3
View File
@@ -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.
[![Star History Chart](https://api.star-history.com/svg?repos=alexjustesen/speedtest-tracker&type=Date)](https://star-history.com/#alexjustesen/speedtest-tracker&Date)
## Introduction
-311
View File
@@ -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 {}
}
+105
View File
@@ -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);
}
}
+10
View File
@@ -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.
+17
View File
@@ -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;
}
}
-39
View File
@@ -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',
];
}
}
+90
View File
@@ -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;
}
}
+9 -1
View File
@@ -92,6 +92,14 @@ class GeneralPage extends SettingsPage
->hint(new HtmlString('&#x1f517;<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([
+126 -83
View File
@@ -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('&#x1f517;<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());
})
+14 -8
View File
@@ -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),
],
]);
}
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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'),
);
}
}
+15
View File
@@ -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';
}
}
+2
View File
@@ -8,6 +8,8 @@ class GeneralSettings extends Settings
{
public bool $auth_enabled;
public int $prune_results_older_than;
public ?string $speedtest_schedule;
/** @var string[] */
-1
View File
@@ -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
View File
@@ -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);
}
};