Merge pull request #1683 from denkfabrik-li/fix/orphan-scanner-preview-renditions

Keep preview renditions out of the orphan-file scan
This commit is contained in:
Ignacio Nelson
2026-08-26 14:05:12 -03:00
committed by GitHub
2 changed files with 27 additions and 8 deletions
+25 -8
View File
@@ -6,6 +6,7 @@ namespace App\Modules\Files;
use App\Models\User;
use App\Modules\Files\Models\File;
use App\Modules\Files\Thumbnails\ImageRendition;
use App\Modules\Files\Uploads\UploadExtensionPolicy;
use App\Modules\Platform\Settings\ExternalStorageConfigApplier;
use App\Modules\Platform\Settings\ExternalStorageSettings;
@@ -20,13 +21,6 @@ use Illuminate\Support\Facades\Storage;
*/
class OrphanFileScanner
{
// Derived artifacts written by FileThumbnailController and
// BuildZipDownloadJob respectively — never orphaned uploads, so
// never candidates regardless of what's in the files table.
// Thumbnails are always local; zips would be too if that job ever
// ran against 'files_external', so the exclusion applies per-disk.
private const EXCLUDED_PREFIXES = ['thumbnails/', 'zips/'];
public function __construct(
private readonly UploadExtensionPolicy $extensionPolicy,
private readonly ExternalStorageConfigApplier $externalStorage,
@@ -160,9 +154,32 @@ class OrphanFileScanner
));
}
/**
* Path prefixes that are derived artifacts, never orphaned uploads, so
* never candidates regardless of what's in the files table: every image
* rendition's cache directory (taken from ImageRendition so a new
* rendition can't be forgotten here — previews used to be) plus the
* download-bundle job's 'zips'. Thumbnails and previews are always local;
* zips would be too if that job ever ran against 'files_external', so the
* exclusion applies per-disk.
*
* @return list<string>
*/
private function excludedPrefixes(): array
{
$prefixes = array_map(
static fn (ImageRendition $rendition): string => $rendition->directory().'/',
ImageRendition::cases(),
);
$prefixes[] = 'zips/';
return $prefixes;
}
private function isExcluded(string $path): bool
{
foreach (self::EXCLUDED_PREFIXES as $prefix) {
foreach ($this->excludedPrefixes() as $prefix) {
if (str_starts_with($path, $prefix)) {
return true;
}
+2
View File
@@ -44,6 +44,8 @@ function orphanDelete(array $items): TestResponse
test('scanning excludes derived-artifact prefixes and anything already claimed by a file row, including soft-deleted ones', function () {
makeOrphanFile('2026/07/orphan.pdf');
makeOrphanFile('thumbnails/2026/07/some.jpg');
makeOrphanFile('previews/2026/07/some.jpg');
makeOrphanFile('previews/external/some.jpg');
makeOrphanFile('zips/some.zip');
$adopted = makeAdoptedFile($this->admin, '2026/07/adopted.pdf');