-
-
Notifications
You must be signed in to change notification settings - Fork 326
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
26 changed files
with
610 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?php | ||
|
||
namespace App\Enum; | ||
|
||
/** | ||
* Enum ConfigType. | ||
* | ||
* The most important type possibilities. | ||
*/ | ||
enum CacheTag: string | ||
{ | ||
case GALLERY = 'gallery'; | ||
case AUTH = 'auth'; | ||
case USER = 'user'; | ||
case SETTINGS = 'settings'; | ||
case STATISTICS = 'statistics'; | ||
case USERS = 'users'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
namespace App\Events; | ||
|
||
use Illuminate\Broadcasting\InteractsWithSockets; | ||
use Illuminate\Foundation\Events\Dispatchable; | ||
use Illuminate\Queue\SerializesModels; | ||
|
||
class AlbumRouteCacheUpdated | ||
{ | ||
use Dispatchable; | ||
use InteractsWithSockets; | ||
use SerializesModels; | ||
|
||
/** | ||
* Create a new event instance. | ||
*/ | ||
public function __construct(public ?string $album_id = null) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
namespace App\Events; | ||
|
||
use App\Enum\CacheTag; | ||
use Illuminate\Broadcasting\InteractsWithSockets; | ||
use Illuminate\Foundation\Events\Dispatchable; | ||
use Illuminate\Queue\SerializesModels; | ||
|
||
class TaggedRouteCacheUpdated | ||
{ | ||
use Dispatchable; | ||
use InteractsWithSockets; | ||
use SerializesModels; | ||
|
||
/** | ||
* Create a new event instance. | ||
*/ | ||
public function __construct(public CacheTag $tag) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
app/Http/Middleware/Caching/AlbumRouteCacheRefresher.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
<?php | ||
|
||
namespace App\Http\Middleware\Caching; | ||
|
||
use App\Contracts\Http\Requests\RequestAttribute; | ||
use App\Events\AlbumRouteCacheUpdated; | ||
use App\Models\Configs; | ||
use Illuminate\Foundation\Http\Middleware\Concerns\ExcludesPaths; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Support\Facades\DB; | ||
use Symfony\Component\HttpFoundation\Response; | ||
|
||
/** | ||
* Response caching, this allows to speed up the reponse time of Lychee by hopefully a lot. | ||
*/ | ||
class AlbumRouteCacheRefresher | ||
{ | ||
use ExcludesPaths; | ||
|
||
/** @var string[] */ | ||
protected array $except = [ | ||
'api/v2/Album', | ||
'api/v2/Album::unlock', | ||
'api/v2/Album::rename', | ||
'api/v2/Album::updateProtectionPolicy', | ||
'api/v2/Album::move', | ||
'api/v2/Album::cover', | ||
'api/v2/Album::header', | ||
'api/v2/Album::merge', | ||
'api/v2/Album::transfer', | ||
'api/v2/Album::track', | ||
'api/v2/TagAlbum', | ||
'api/v2/Sharing', | ||
'api/v2/Photo::fromUrl', | ||
'api/v2/Photo', | ||
'api/v2/Photo::rename', | ||
'api/v2/Photo::tags', | ||
'api/v2/Photo::move', | ||
'api/v2/Photo::copy', | ||
'api/v2/Photo::star', | ||
'api/v2/Photo::rotate', | ||
]; | ||
|
||
/** | ||
* Handle an incoming request. | ||
* | ||
* @param Request $request | ||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next | ||
* | ||
* @return Response | ||
* | ||
* @throws \InvalidArgumentException | ||
*/ | ||
public function handle(Request $request, \Closure $next): mixed | ||
{ | ||
if ($request->method() === 'GET') { | ||
return $next($request); | ||
} | ||
|
||
if (Configs::getValueAsBool('cache_enabled') === false) { | ||
return $next($request); | ||
} | ||
|
||
// ! We use $except as a ALLOW list instead of a DENY list | ||
if (!$this->inExceptArray($request)) { | ||
return $next($request); | ||
} | ||
|
||
$full_album_ids = collect(); | ||
|
||
/** @var string|null $album_id */ | ||
$album_id = $request->input(RequestAttribute::ALBUM_ID_ATTRIBUTE); | ||
if ($album_id !== null) { | ||
$full_album_ids->push($album_id); | ||
} | ||
|
||
/** @var string[]|null */ | ||
$albums_id = $request->input(RequestAttribute::ALBUM_IDS_ATTRIBUTE); | ||
if ($albums_id !== null) { | ||
$full_album_ids = $full_album_ids->merge($albums_id); | ||
} | ||
|
||
/** @var string|null */ | ||
$parent_id = $request->input(RequestAttribute::PARENT_ID_ATTRIBUTE); | ||
if ($parent_id !== null) { | ||
$full_album_ids->push($parent_id); | ||
} | ||
|
||
/** @var string|null */ | ||
$photo_id = $request->input(RequestAttribute::PHOTO_ID_ATTRIBUTE); | ||
/** @var string[]|null */ | ||
$photo_ids = $request->input(RequestAttribute::PHOTO_IDS_ATTRIBUTE); | ||
|
||
if ($photo_ids !== null || $photo_id !== null) { | ||
$photos_album_ids = DB::table('photos') | ||
->select('album_id') | ||
->whereIn('id', $photo_ids ?? []) | ||
->orWhere('id', '=', $photo_id) | ||
->distinct() | ||
->pluck('album_id') | ||
->all(); | ||
if (count($photos_album_ids) > 0) { | ||
$full_album_ids = $full_album_ids->merge($photos_album_ids); | ||
} | ||
} | ||
|
||
if ($albums_id !== null || $album_id !== null) { | ||
$albums_parents_ids = DB::table('albums') | ||
->select('parent_id') | ||
->whereIn('id', $albums_id ?? []) | ||
->orWhere('id', '=', $album_id) | ||
->distinct() | ||
->pluck('parent_id') | ||
->all(); | ||
if (count($albums_parents_ids) > 0) { | ||
$full_album_ids = $full_album_ids->merge($albums_parents_ids); | ||
} | ||
} | ||
|
||
$full_album_ids->each(fn ($album_id) => AlbumRouteCacheUpdated::dispatch($album_id ?? '')); | ||
|
||
return $next($request); | ||
} | ||
} |
4 changes: 1 addition & 3 deletions
4
app/Http/Middleware/CacheControl.php → app/Http/Middleware/Caching/CacheControl.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<?php | ||
|
||
namespace App\Listeners; | ||
|
||
use App\Enum\CacheTag; | ||
use App\Enum\SmartAlbumType; | ||
use App\Events\AlbumRouteCacheUpdated; | ||
use App\Metadata\Cache\RouteCacheManager; | ||
use App\Models\BaseAlbumImpl; | ||
use Illuminate\Support\Facades\Cache; | ||
|
||
class AlbumCacheCleaner | ||
{ | ||
/** | ||
* Create the event listener. | ||
*/ | ||
public function __construct( | ||
private RouteCacheManager $route_cache_manager, | ||
) { | ||
} | ||
|
||
/** | ||
* Handle the event. | ||
*/ | ||
public function handle(AlbumRouteCacheUpdated $event): void | ||
{ | ||
// The quick way. | ||
if (Cache::supportsTags()) { | ||
Cache::tags(CacheTag::GALLERY->value)->flush(); | ||
|
||
return; | ||
} | ||
|
||
$this->dropCachedRoutesWithoutExtra(); | ||
|
||
// By default we refresh all the smart albums. | ||
$this->handleSmartAlbums(); | ||
|
||
if ($event->album_id === null) { | ||
$this->handleAllAlbums(); | ||
|
||
return; | ||
} | ||
|
||
// Root album => already taken care of with the route without extra. | ||
if ($event->album_id === '') { | ||
return; | ||
} | ||
|
||
$this->handleAlbumId($event->album_id); | ||
} | ||
|
||
/** | ||
* Drop cache for all routes without extra (meaning which do not depend on album_id). | ||
* | ||
* @return void | ||
*/ | ||
private function dropCachedRoutesWithoutExtra(): void | ||
{ | ||
$cached_routes_without_extra = $this->route_cache_manager->retrieve_keys_for_tag(CacheTag::GALLERY, without_extra: true); | ||
foreach ($cached_routes_without_extra as $route) { | ||
$cache_key = $this->route_cache_manager->gen_key(uri: $route); | ||
Cache::forget($cache_key); | ||
} | ||
} | ||
|
||
/** | ||
* Drop cache for all routes related to albums. | ||
* | ||
* @return void | ||
*/ | ||
private function handleAllAlbums(): void | ||
{ | ||
// The long way. | ||
$cached_routes_with_extra = $this->route_cache_manager->retrieve_keys_for_tag(CacheTag::GALLERY, with_extra: true); | ||
BaseAlbumImpl::select('id')->get()->each(function (BaseAlbumImpl $album) use ($cached_routes_with_extra) { | ||
$extra = ['album_id' => $album->id]; | ||
foreach ($cached_routes_with_extra as $route) { | ||
$cache_key = $this->route_cache_manager->gen_key(uri: $route, extras: $extra); | ||
Cache::forget($cache_key); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Drop cache fro all routes related to an album. | ||
* | ||
* @param string $album_id | ||
* | ||
* @return void | ||
*/ | ||
private function handleAlbumId(string $album_id): void | ||
{ | ||
$cached_routes_with_extra = $this->route_cache_manager->retrieve_keys_for_tag(CacheTag::GALLERY, with_extra: true); | ||
$extra = ['album_id' => $album_id]; | ||
|
||
foreach ($cached_routes_with_extra as $route) { | ||
$cache_key = $this->route_cache_manager->gen_key(uri: $route, extras: $extra); | ||
Cache::forget($cache_key); | ||
} | ||
} | ||
|
||
/** | ||
* Drop cache for all smart albums too. | ||
* | ||
* @return void | ||
*/ | ||
private function handleSmartAlbums(): void | ||
{ | ||
$cached_routes_with_extra = $this->route_cache_manager->retrieve_keys_for_tag(CacheTag::GALLERY, with_extra: true); | ||
// Also reset smart albums ;) | ||
collect(SmartAlbumType::cases())->each(function (SmartAlbumType $type) use ($cached_routes_with_extra) { | ||
$extra = ['album_id' => $type->value]; | ||
foreach ($cached_routes_with_extra as $route) { | ||
$cache_key = $this->route_cache_manager->gen_key(uri: $route, extras: $extra); | ||
Cache::forget($cache_key); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
namespace App\Listeners; | ||
|
||
use App\Events\TaggedRouteCacheUpdated; | ||
use App\Metadata\Cache\RouteCacheManager; | ||
use Illuminate\Support\Facades\Cache; | ||
|
||
class TaggedRouteCacheCleaner | ||
{ | ||
/** | ||
* Create the event listener. | ||
*/ | ||
public function __construct( | ||
private RouteCacheManager $route_cache_manager, | ||
) { | ||
} | ||
|
||
/** | ||
* Handle the event. | ||
*/ | ||
public function handle(TaggedRouteCacheUpdated $event): void | ||
{ | ||
$cached_routes = $this->route_cache_manager->retrieve_keys_for_tag($event->tag); | ||
|
||
foreach ($cached_routes as $route) { | ||
$cache_key = $this->route_cache_manager->gen_key($route); | ||
Cache::forget($cache_key); | ||
} | ||
|
||
if (Cache::supportsTags()) { | ||
Cache::tags($event->tag->value)->flush(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
|
||
/** | ||
* We don't care for unhandled exceptions in tests. | ||
* It is the nature of a test to throw an exception. | ||
* Without this suppression we had 100+ Linter warning in this file which | ||
* don't help anything. | ||
* | ||
* @noinspection PhpDocMissingThrowsInspection | ||
* @noinspection PhpUnhandledExceptionInspection | ||
*/ | ||
|
||
namespace Tests\Unit\Metadata\Cache; | ||
|
||
use App\Enum\CacheTag; | ||
use App\Metadata\Cache\RouteCacheManager; | ||
use Illuminate\Foundation\Testing\DatabaseTransactions; | ||
use Illuminate\Support\Facades\Log; | ||
use Tests\AbstractTestCase; | ||
|
||
class RouteCacheManagerTest extends AbstractTestCase | ||
{ | ||
use DatabaseTransactions; | ||
private RouteCacheManager $route_cache_manager; | ||
|
||
public function setUp(): void | ||
{ | ||
parent::setUp(); | ||
$this->route_cache_manager = new RouteCacheManager(); | ||
} | ||
|
||
public function testNoConfig(): void | ||
{ | ||
Log::shouldReceive('warning')->once(); | ||
$this->assertFalse($this->route_cache_manager->get_config('fake_url')); | ||
} | ||
|
||
public function testConfigFalse(): void | ||
{ | ||
$this->assertFalse($this->route_cache_manager->get_config('api/v2/Version')); | ||
} | ||
|
||
public function testConfigValid(): void | ||
{ | ||
$this->assertIsObject($this->route_cache_manager->get_config('api/v2/Album')); | ||
} | ||
|
||
public function testGenKey(): void | ||
{ | ||
$this->assertEquals('R:api/v2/Albums|U:', $this->route_cache_manager->gen_key('api/v2/Albums')); | ||
$this->assertEquals('R:api/v2/Albums|U:1', $this->route_cache_manager->gen_key('api/v2/Albums', 1)); | ||
$this->assertEquals('R:api/v2/Album|U:1|E::2', $this->route_cache_manager->gen_key('api/v2/Album', 1, ['album_id' => '2'])); | ||
} | ||
|
||
public function testGetFromTag(): void | ||
{ | ||
$routes = $this->route_cache_manager->retrieve_keys_for_tag(CacheTag::GALLERY); | ||
$this->assertIsArray($routes); | ||
$this->assertContains('api/v2/Album', $routes); | ||
} | ||
|
||
public function testGetFromTagWithExtra(): void | ||
{ | ||
$routes = $this->route_cache_manager->retrieve_keys_for_tag(CacheTag::GALLERY, with_extra: true); | ||
$this->assertIsArray($routes); | ||
$this->assertContains('api/v2/Album', $routes); | ||
$this->assertNotContains('api/v2/Albums', $routes); | ||
} | ||
|
||
public function testGetFromTagWithOutExtra(): void | ||
{ | ||
$routes = $this->route_cache_manager->retrieve_keys_for_tag(CacheTag::GALLERY, without_extra: true); | ||
$this->assertIsArray($routes); | ||
$this->assertContains('api/v2/Albums', $routes); | ||
$this->assertNotContains('api/v2/Album', $routes); | ||
} | ||
} | ||
|