From e70057e3badd3604752d76d6dcc360e802c4200e Mon Sep 17 00:00:00 2001 From: lukinovec Date: Fri, 21 Aug 2026 14:16:40 +0200 Subject: [PATCH] Read the tenant asset root from the resolved disk instead of the disk config Also, instead of throwing the "no root path configured" exception, just throw an exception if the disk is not local (i.e. is not instanceof LocalFilesystemAdapter). A local disk HAS to have a string root, otherwise, Laravel throws an exception while instantiating that disk. --- src/Controllers/TenantAssetController.php | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Controllers/TenantAssetController.php b/src/Controllers/TenantAssetController.php index 7b80cc2b..5c74b790 100644 --- a/src/Controllers/TenantAssetController.php +++ b/src/Controllers/TenantAssetController.php @@ -9,6 +9,8 @@ use Exception; use Illuminate\Http\Request; use Illuminate\Routing\Controllers\HasMiddleware; use Illuminate\Routing\Controllers\Middleware; +use Illuminate\Filesystem\LocalFilesystemAdapter; +use Illuminate\Support\Facades\Storage; use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Throwable; @@ -43,9 +45,13 @@ class TenantAssetController implements HasMiddleware * * When null, the assets are served from app/public inside the tenant's storage directory. * - * The disk has to be local and have a root configured, since the assets are read from the filesystem. - * It should also be listed in tenancy.filesystem.disks. FilesystemTenancyBootstrapper only scopes - * the roots of disks listed there, so otherwise every tenant is served the same central directory. + * The disk has to be local, since the assets are read from the filesystem. Disks using the + * 'scoped' driver are supported as long as their parent disk uses the 'local' driver. + * + * It should also be listed in tenancy.filesystem.disks -- for scoped disks, it's the parent + * disk that has to be listed there (since a scoped disk inherits the parent's root). + * FilesystemTenancyBootstrapper only scopes the roots of disks listed there, so otherwise + * every tenant is served the same (central) directory. */ public static string|null $publicDisk = null; @@ -89,14 +95,17 @@ class TenantAssetController implements HasMiddleware protected function assetRoot(): string { if (static::$publicDisk) { - $diskRoot = config('filesystems.disks.' . static::$publicDisk . '.root'); + $disk = Storage::disk(static::$publicDisk); - if (! is_string($diskRoot)) { - // A disk with no root path would let the controller serve any file in the app - throw new Exception('Disk [' . static::$publicDisk . '] has no root path configured.'); + if (! $disk instanceof LocalFilesystemAdapter) { + // The root has to be read from the resolved disk rather than from the disk's config, + // since the config root isn't the full root path of every local disk. Disks using the + // 'scoped' driver have no root in their config -- they inherit their parent disk's root -- + // and a 'prefix' is part of the root path as well. + throw new Exception('Disk [' . static::$publicDisk . '] is not a local disk. Only local disks can be used for serving assets.'); } - return rtrim($diskRoot, '/'); + return rtrim($disk->path(''), DIRECTORY_SEPARATOR); } if ($tenant = tenant()) {