diff --git a/app/Actions/Diagnostics/Errors.php b/app/Actions/Diagnostics/Errors.php index 61c8b3af402..572904045b3 100644 --- a/app/Actions/Diagnostics/Errors.php +++ b/app/Actions/Diagnostics/Errors.php @@ -28,6 +28,7 @@ use App\Actions\Diagnostics\Pipes\Checks\PHPVersionCheck; use App\Actions\Diagnostics\Pipes\Checks\PlaceholderExistsCheck; use App\Actions\Diagnostics\Pipes\Checks\SmallMediumExistsCheck; +use App\Actions\Diagnostics\Pipes\Checks\StatisticsIntegrityCheck; use App\Actions\Diagnostics\Pipes\Checks\SupporterCheck; use App\Actions\Diagnostics\Pipes\Checks\TimezoneCheck; use App\Actions\Diagnostics\Pipes\Checks\UpdatableCheck; @@ -65,6 +66,7 @@ class Errors CacheTemporaryUrlCheck::class, SupporterCheck::class, ImagickPdfCheck::class, + StatisticsIntegrityCheck::class, ]; /** diff --git a/app/Actions/Diagnostics/Pipes/Checks/StatisticsIntegrityCheck.php b/app/Actions/Diagnostics/Pipes/Checks/StatisticsIntegrityCheck.php new file mode 100644 index 00000000000..7a6b2a91ac0 --- /dev/null +++ b/app/Actions/Diagnostics/Pipes/Checks/StatisticsIntegrityCheck.php @@ -0,0 +1,58 @@ +get(); + + if ($check->missing_albums > 0) { + $data[] = DiagnosticData::warn(sprintf('There are %d albums without statistics.', $check->missing_albums), self::class, + ['Go to the maintenance page to fix this.']); + } + + if ($check->missing_albums > 0) { + $data[] = DiagnosticData::warn(sprintf('There are %d photos without statistics.', $check->missing_photos), self::class, + ['Go to the maintenance page to fix this.']); + } + + return $next($data); + } + + public function get(): StatisticsCheckResource + { + // Just skip the check, we don't care. + if (!Configs::getValueAsBool('metrics_enabled')) { + return new StatisticsCheckResource(0, 0); + } + + $num_albums = DB::table('base_albums')->leftJoin('statistics', 'base_albums.id', '=', 'statistics.album_id') + ->whereNull('statistics.id') + ->count(); + $num_photos = DB::table('photos')->leftJoin('statistics', 'photos.id', '=', 'statistics.photo_id') + ->whereNull('statistics.id') + ->count(); + + return new StatisticsCheckResource($num_albums, $num_photos); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Admin/Maintenance/StatisticsCheck.php b/app/Http/Controllers/Admin/Maintenance/StatisticsCheck.php new file mode 100644 index 00000000000..8e5a923904c --- /dev/null +++ b/app/Http/Controllers/Admin/Maintenance/StatisticsCheck.php @@ -0,0 +1,56 @@ +check->get(); + } + + /** + * Check how many statistics are missing for photos and albums. + * + * @return StatisticsCheckResource + */ + public function check(MaintenanceRequest $request): StatisticsCheckResource + { + return $this->check->get(); + } +} diff --git a/app/Http/Resources/Diagnostics/StatisticsCheckResource.php b/app/Http/Resources/Diagnostics/StatisticsCheckResource.php new file mode 100644 index 00000000000..cf4996e594e --- /dev/null +++ b/app/Http/Resources/Diagnostics/StatisticsCheckResource.php @@ -0,0 +1,22 @@ +visit_count, $stats->download_count, diff --git a/app/Metadata/Cache/RouteCacheManager.php b/app/Metadata/Cache/RouteCacheManager.php index abbf4e99d96..e2c45cba4ee 100644 --- a/app/Metadata/Cache/RouteCacheManager.php +++ b/app/Metadata/Cache/RouteCacheManager.php @@ -71,6 +71,7 @@ public function __construct() 'api/v2/Maintenance::update' => false, 'api/v2/Maintenance::countDuplicates' => false, 'api/v2/Maintenance::searchDuplicates' => false, + 'api/v2/Maintenance::statisticsIntegrity' => false, 'api/v2/Map' => new RouteCacheConfig(tag: CacheTag::GALLERY, user_dependant: true, extra: [RequestAttribute::ALBUM_ID_ATTRIBUTE]), 'api/v2/Map::provider' => new RouteCacheConfig(tag: CacheTag::SETTINGS), diff --git a/lang/ar/maintenance.php b/lang/ar/maintenance.php index 85edee51788..a6e62ecebdd 100644 --- a/lang/ar/maintenance.php +++ b/lang/ar/maintenance.php @@ -58,6 +58,12 @@ 'update-button' => '', 'no-pending-updates' => '', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => '', 'description' => '', diff --git a/lang/cz/maintenance.php b/lang/cz/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/cz/maintenance.php +++ b/lang/cz/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/de/maintenance.php b/lang/de/maintenance.php index 78a93a7baec..8a028396fe2 100644 --- a/lang/de/maintenance.php +++ b/lang/de/maintenance.php @@ -57,6 +57,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'Keine Updates verfügbar.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Cache leeren', 'description' => 'Leeren Sie den Cache jedes Benutzers, um Ungültigkeitsprobleme zu lösen.', diff --git a/lang/el/maintenance.php b/lang/el/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/el/maintenance.php +++ b/lang/el/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/en/maintenance.php b/lang/en/maintenance.php index 06d45165cb7..ec8d43f34ab 100644 --- a/lang/en/maintenance.php +++ b/lang/en/maintenance.php @@ -58,6 +58,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/es/maintenance.php b/lang/es/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/es/maintenance.php +++ b/lang/es/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/fr/maintenance.php b/lang/fr/maintenance.php index e44c16db703..5c30890fbda 100644 --- a/lang/fr/maintenance.php +++ b/lang/fr/maintenance.php @@ -58,6 +58,12 @@ 'update-button' => 'Mettre à jour', 'no-pending-updates' => 'Aucune mise à jour en attente.', ], + 'statistics-check' => [ + 'title' => 'Check de l’intégrité des Statistiques', + 'missing_photos' => '%d statistiques de photos manquantes.', + 'missing_albums' => '%d statistiques d’albums manquantes.', + 'button' => 'Créer les statistiques manquantes', + ], 'flush-cache' => [ 'title' => 'Vider le cache', 'description' => 'Vider le cache de tous les utilisateurs pour résoudre les problèmes d’invalidation.', diff --git a/lang/hu/maintenance.php b/lang/hu/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/hu/maintenance.php +++ b/lang/hu/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/it/maintenance.php b/lang/it/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/it/maintenance.php +++ b/lang/it/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/ja/maintenance.php b/lang/ja/maintenance.php index 5faab934124..c056179bfe0 100644 --- a/lang/ja/maintenance.php +++ b/lang/ja/maintenance.php @@ -58,6 +58,12 @@ 'update-button' => '更新', 'no-pending-updates' => '保留中の更新はありません', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/nl/maintenance.php b/lang/nl/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/nl/maintenance.php +++ b/lang/nl/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/no/maintenance.php b/lang/no/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/no/maintenance.php +++ b/lang/no/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/pl/maintenance.php b/lang/pl/maintenance.php index 5b63e1774b7..d0dcfe0e70a 100644 --- a/lang/pl/maintenance.php +++ b/lang/pl/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Aktualizacja', 'no-pending-updates' => 'Brak oczekujących aktualizacji.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Opróżnianie pamięci podręcznej', 'description' => 'Opróżnianie pamięci podręcznej każdego użytkownika w celu rozwiązania problemów z unieważnianiem.', diff --git a/lang/pt/maintenance.php b/lang/pt/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/pt/maintenance.php +++ b/lang/pt/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/ru/maintenance.php b/lang/ru/maintenance.php index 74f88ee90a8..a4113a617b2 100644 --- a/lang/ru/maintenance.php +++ b/lang/ru/maintenance.php @@ -58,6 +58,12 @@ 'update-button' => 'Обновить', 'no-pending-updates' => 'Нет ожидающих обновлений.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Очистить кэш', 'description' => 'Очистить кэш каждого пользователя для решения проблем с устаревшими данными.', diff --git a/lang/sk/maintenance.php b/lang/sk/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/sk/maintenance.php +++ b/lang/sk/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/sv/maintenance.php b/lang/sv/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/sv/maintenance.php +++ b/lang/sv/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/vi/maintenance.php b/lang/vi/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/vi/maintenance.php +++ b/lang/vi/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/lang/zh_CN/maintenance.php b/lang/zh_CN/maintenance.php index 1e402f947ad..ab16be1079f 100644 --- a/lang/zh_CN/maintenance.php +++ b/lang/zh_CN/maintenance.php @@ -58,6 +58,12 @@ 'update-button' => '更新', 'no-pending-updates' => '没有待处理的更新。', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => '清除缓存', 'description' => '清除所有用户的缓存以解决失效问题。', diff --git a/lang/zh_TW/maintenance.php b/lang/zh_TW/maintenance.php index d059c20a88f..a1ceea32a13 100644 --- a/lang/zh_TW/maintenance.php +++ b/lang/zh_TW/maintenance.php @@ -59,6 +59,12 @@ 'update-button' => 'Update', 'no-pending-updates' => 'No pending update.', ], + 'statistics-check' => [ + 'title' => 'Statistics integrity Check', + 'missing_photos' => '%d photo statistics missing.', + 'missing_albums' => '%d album statistics missing.', + 'button' => 'Create missing', + ], 'flush-cache' => [ 'title' => 'Flush Cache', 'description' => 'Flush the cache of every user to solve invalidation problems.', diff --git a/resources/js/components/maintenance/StatisticsIntegrity.vue b/resources/js/components/maintenance/StatisticsIntegrity.vue new file mode 100644 index 00000000000..3245ea96913 --- /dev/null +++ b/resources/js/components/maintenance/StatisticsIntegrity.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/resources/js/lychee.d.ts b/resources/js/lychee.d.ts index 19db5edf16e..e9f8cabf3bc 100644 --- a/resources/js/lychee.d.ts +++ b/resources/js/lychee.d.ts @@ -148,6 +148,10 @@ declare namespace App.Http.Resources.Diagnostics { left: string; right: string; }; + export type StatisticsCheckResource = { + missing_photos: number; + missing_albums: number; + }; export type TreeState = { oddness: number; duplicates: number; diff --git a/resources/js/services/maintenance-service.ts b/resources/js/services/maintenance-service.ts index f334191ab55..6b441c8281a 100644 --- a/resources/js/services/maintenance-service.ts +++ b/resources/js/services/maintenance-service.ts @@ -88,6 +88,14 @@ const MaintenanceService = { }, }); }, + + statisticsIntegrityCheckGet(): Promise> { + return axios.get(`${Constants.getApiUrl()}Maintenance::statisticsIntegrity`, { data: {} }); + }, + + statisticsIntegrityCheckDo(): Promise { + return axios.post(`${Constants.getApiUrl()}Maintenance::statisticsIntegrity`, {}); + }, }; export default MaintenanceService; diff --git a/resources/js/views/Maintenance.vue b/resources/js/views/Maintenance.vue index ef7cbeacae3..0fa18373c1e 100644 --- a/resources/js/views/Maintenance.vue +++ b/resources/js/views/Maintenance.vue @@ -28,6 +28,7 @@ + @@ -45,4 +46,5 @@ import MaintenanceOptimize from "@/components/maintenance/MaintenanceOptimize.vu import MaintenanceUpdate from "@/components/maintenance/MaintenanceUpdate.vue"; import MaintenanceFlushCache from "@/components/maintenance/MaintenanceFlushCache.vue"; import OpenLeftMenu from "@/components/headers/OpenLeftMenu.vue"; +import StatisticsIntegrity from "@/components/maintenance/StatisticsIntegrity.vue"; diff --git a/routes/api_v2.php b/routes/api_v2.php index 08a7e5c49ae..f196fd59993 100644 --- a/routes/api_v2.php +++ b/routes/api_v2.php @@ -221,6 +221,8 @@ Route::post('/Maintenance::fullTree', [Admin\Maintenance\FullTree::class, 'do']); Route::get('/Maintenance::countDuplicates', [Admin\Maintenance\DuplicateFinder::class, 'check']); Route::get('/Maintenance::searchDuplicates', [Admin\Maintenance\DuplicateFinder::class, 'get']); +Route::get('/Maintenance::statisticsIntegrity', [Admin\Maintenance\StatisticsCheck::class, 'check']); +Route::post('/Maintenance::statisticsIntegrity', [Admin\Maintenance\StatisticsCheck::class, 'do']); /** * STATISTICS. diff --git a/tests/Feature_v2/Maintenance/StatisticsIntegrityTest.php b/tests/Feature_v2/Maintenance/StatisticsIntegrityTest.php new file mode 100644 index 00000000000..486aa4a7b1c --- /dev/null +++ b/tests/Feature_v2/Maintenance/StatisticsIntegrityTest.php @@ -0,0 +1,89 @@ +getJsonWithData('Maintenance::statisticsIntegrity', []); + $this->assertUnauthorized($response); + + $response = $this->postJson('Maintenance::statisticsIntegrity', []); + $this->assertUnauthorized($response); + } + + public function testUser(): void + { + $response = $this->actingAs($this->userLocked)->getJsonWithData('Maintenance::statisticsIntegrity', []); + $this->assertForbidden($response); + + $response = $this->actingAs($this->userLocked)->postJson('Maintenance::statisticsIntegrity', []); + $this->assertForbidden($response); + } + + public function testAdmin(): void + { + DB::table('statistics')->truncate(); + + $response = $this->actingAs($this->admin)->getJsonWithData('Maintenance::statisticsIntegrity', []); + $this->assertOk($response); + $response->assertJsonPath('missing_albums', 9); + $response->assertJsonPath('missing_photos', 9); + + $response = $this->actingAs($this->admin)->postJson('Maintenance::statisticsIntegrity', []); + $this->assertCreated($response); + $response->assertJsonPath('missing_albums', 0); + $response->assertJsonPath('missing_photos', 0); + } + + public function testAdminWithDisabledMetrics(): void + { + Configs::set('metrics_enabled', false); + Configs::invalidateCache(); + DB::table('statistics')->truncate(); + $response = $this->actingAs($this->admin)->getJsonWithData('Maintenance::statisticsIntegrity', []); + $this->assertOk($response); + $response->assertJsonPath('missing_albums', 0); + $response->assertJsonPath('missing_photos', 0); + + $response = $this->actingAs($this->admin)->postJson('Maintenance::statisticsIntegrity', []); + $this->assertCreated($response); + $response->assertJsonPath('missing_albums', 0); + $response->assertJsonPath('missing_photos', 0); + } +} \ No newline at end of file