From 057620b7153e40c1f7abc98339292fe980eaac67 Mon Sep 17 00:00:00 2001 From: fatihalp Date: Thu, 19 Mar 2026 23:42:57 +0300 Subject: [PATCH] Remove S3 storage support and standardize local media handling --- .../Filament/Pages/ManageGeneralSettings.php | 51 +----- Modules/Admin/Support/HomeSlideFormSchema.php | 27 +--- Modules/Listing/Models/Listing.php | 63 +++++++- .../Filament/AdminListingResourceSchema.php | 6 +- .../App/Livewire/PanelQuickListingForm.php | 13 +- Modules/S3/Providers/S3ServiceProvider.php | 13 -- Modules/S3/Support/MediaStorage.php | 145 ------------------ Modules/S3/config/s3.php | 7 - Modules/S3/module.json | 12 -- .../App/Providers/SiteServiceProvider.php | 15 ++ Modules/Site/App/Settings/GeneralSettings.php | 4 - .../Site/App/Support/HomeSlideDefaults.php | 12 +- Modules/Site/App/Support/LocalMedia.php | 55 +++++++ Modules/Site/App/Support/RequestAppData.php | 14 +- .../App/Support/ScopedMediaPathGenerator.php | 34 ++++ Modules/Site/resources/views/home.blade.php | 2 +- Modules/User/App/Models/User.php | 21 ++- Modules/Video/Models/Video.php | 24 +-- .../Support/Filament/VideoFormSchema.php | 6 +- Modules/Video/Support/VideoTranscoder.php | 4 +- Modules/Video/config/video.php | 2 +- composer.json | 2 - config/filemanager.php | 11 +- config/filesystems.php | 16 +- ...6_03_03_120000_create_general_settings.php | 5 +- modules_statuses.json | 1 - 26 files changed, 224 insertions(+), 341 deletions(-) delete mode 100644 Modules/S3/Providers/S3ServiceProvider.php delete mode 100644 Modules/S3/Support/MediaStorage.php delete mode 100644 Modules/S3/config/s3.php delete mode 100644 Modules/S3/module.json create mode 100644 Modules/Site/App/Support/LocalMedia.php create mode 100644 Modules/Site/App/Support/ScopedMediaPathGenerator.php diff --git a/Modules/Admin/Filament/Pages/ManageGeneralSettings.php b/Modules/Admin/Filament/Pages/ManageGeneralSettings.php index 53328ef87..f830517a9 100644 --- a/Modules/Admin/Filament/Pages/ManageGeneralSettings.php +++ b/Modules/Admin/Filament/Pages/ManageGeneralSettings.php @@ -4,21 +4,18 @@ namespace Modules\Admin\Filament\Pages; use BackedEnum; use Filament\Forms\Components\FileUpload; -use Filament\Forms\Components\Hidden; use Filament\Forms\Components\Select; use Filament\Forms\Components\TagsInput; use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; use Filament\Pages\SettingsPage; -use Filament\Schemas\Components\Utilities\Get; -use Filament\Schemas\Components\Utilities\Set; use Filament\Schemas\Schema; use Modules\Admin\Support\HomeSlideFormSchema; use Modules\Location\Support\CountryCodeManager; -use Modules\S3\Support\MediaStorage; use Modules\Site\App\Settings\GeneralSettings; use Modules\Site\App\Support\HomeSlideDefaults; +use Modules\Site\App\Support\LocalMedia; use Tapp\FilamentCountryCodeField\Forms\Components\CountryCodeSelect; use UnitEnum; use Ysfkaya\FilamentPhoneInput\Forms\PhoneInput; @@ -44,15 +41,8 @@ class ManageGeneralSettings extends SettingsPage return [ 'site_name' => filled($data['site_name'] ?? null) ? $data['site_name'] : $defaults['site_name'], 'site_description' => filled($data['site_description'] ?? null) ? $data['site_description'] : $defaults['site_description'], - 'media_disk' => MediaStorage::normalizeDriver($data['media_disk'] ?? $defaults['media_disk']), - 'home_slides' => $this->normalizeHomeSlides( - $data['home_slides'] ?? $defaults['home_slides'], - MediaStorage::storedDisk('public'), - ), + 'home_slides' => $this->normalizeHomeSlides($data['home_slides'] ?? $defaults['home_slides']), 'site_logo' => $data['site_logo'] ?? null, - 'site_logo_disk' => filled($data['site_logo'] ?? null) - ? MediaStorage::storedDisk($data['site_logo_disk'] ?? 'public') - : null, 'sender_name' => filled($data['sender_name'] ?? null) ? $data['sender_name'] : $defaults['sender_name'], 'sender_email' => filled($data['sender_email'] ?? null) ? $data['sender_email'] : $defaults['sender_email'], 'default_language' => filled($data['default_language'] ?? null) ? $data['default_language'] : $defaults['default_language'], @@ -77,14 +67,7 @@ class ManageGeneralSettings extends SettingsPage protected function mutateFormDataBeforeSave(array $data): array { - $mediaDriver = MediaStorage::normalizeDriver($data['media_disk'] ?? null); - $mediaDisk = MediaStorage::diskFromDriver($mediaDriver); - - $data['media_disk'] = $mediaDriver; - $data['home_slides'] = $this->normalizeHomeSlides($data['home_slides'] ?? [], $mediaDisk); - $data['site_logo_disk'] = MediaStorage::managesPath($data['site_logo'] ?? null) - ? MediaStorage::storedDisk($data['site_logo_disk'] ?? null, $mediaDriver) - : null; + $data['home_slides'] = $this->normalizeHomeSlides($data['home_slides'] ?? []); $data['currencies'] = $this->normalizeCurrencies($data['currencies'] ?? []); return $data; @@ -106,32 +89,16 @@ class ManageGeneralSettings extends SettingsPage ->default($defaults['site_description']) ->rows(3) ->maxLength(500), - Select::make('media_disk') - ->label('Media Storage') - ->options(MediaStorage::options()) - ->default($defaults['media_disk']) - ->required() - ->native(false) - ->helperText('Storage driver used for listing images, videos, the site logo, and home slide visuals.'), HomeSlideFormSchema::make( $defaults['home_slides'], - fn ($state): array => $this->normalizeHomeSlides($state, MediaStorage::activeDisk()), + fn ($state): array => $this->normalizeHomeSlides($state), ), - Hidden::make('site_logo_disk'), FileUpload::make('site_logo') ->label('Site Logo') ->image() - ->disk(fn (Get $get): string => MediaStorage::storedDisk($get('site_logo_disk'), $get('media_disk'))) + ->disk(LocalMedia::disk()) ->directory('settings') - ->visibility('public') - ->afterStateUpdated(function (Get $get, Set $set, mixed $state): void { - $set( - 'site_logo_disk', - MediaStorage::managesPath($state) - ? MediaStorage::diskFromDriver($get('media_disk')) - : null, - ); - }), + ->visibility('public'), TextInput::make('sender_name') ->label('Sender Name') ->default($defaults['sender_name']) @@ -242,9 +209,7 @@ class ManageGeneralSettings extends SettingsPage return [ 'site_name' => $siteName, 'site_description' => 'A fast and secure marketplace for buying and selling.', - 'media_disk' => MediaStorage::defaultDriver(), 'home_slides' => $this->defaultHomeSlides(), - 'site_logo_disk' => null, 'sender_name' => $siteName, 'sender_email' => (string) config('mail.from.address', 'info@'.$siteHost), 'default_language' => in_array(config('app.locale'), array_keys($this->localeOptions()), true) ? (string) config('app.locale') : 'en', @@ -292,8 +257,8 @@ class ManageGeneralSettings extends SettingsPage return HomeSlideDefaults::defaults(); } - private function normalizeHomeSlides(mixed $state, ?string $defaultDisk = null): array + private function normalizeHomeSlides(mixed $state): array { - return HomeSlideDefaults::normalize($state, $defaultDisk); + return HomeSlideDefaults::normalize($state); } } diff --git a/Modules/Admin/Support/HomeSlideFormSchema.php b/Modules/Admin/Support/HomeSlideFormSchema.php index 6c108db9a..7c23750bb 100644 --- a/Modules/Admin/Support/HomeSlideFormSchema.php +++ b/Modules/Admin/Support/HomeSlideFormSchema.php @@ -3,13 +3,10 @@ namespace Modules\Admin\Support; use Filament\Forms\Components\FileUpload; -use Filament\Forms\Components\Hidden; use Filament\Forms\Components\Repeater; -use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Textarea; -use Filament\Schemas\Components\Utilities\Get; -use Filament\Schemas\Components\Utilities\Set; -use Modules\S3\Support\MediaStorage; +use Filament\Forms\Components\TextInput; +use Modules\Site\App\Support\LocalMedia; final class HomeSlideFormSchema { @@ -19,24 +16,15 @@ final class HomeSlideFormSchema ->label('Homepage Slides') ->helperText('Use 1 to 5 slides. Upload a wide image for each slide to improve the hero area.') ->schema([ - Hidden::make('disk'), FileUpload::make('image_path') ->label('Slide Image') ->image() - ->disk(fn (Get $get): string => MediaStorage::storedDisk($get('disk'), self::mediaDriver($get))) + ->disk(LocalMedia::disk()) ->directory('home-slides') ->visibility('public') ->imageEditor() ->imagePreviewHeight('200') ->helperText('Recommended: 1600x1000 or wider.') - ->afterStateUpdated(function (Get $get, Set $set, mixed $state): void { - $set( - 'disk', - MediaStorage::managesPath($state) - ? MediaStorage::diskFromDriver(self::mediaDriver($get)) - : null, - ); - }) ->columnSpanFull(), TextInput::make('badge') ->label('Badge') @@ -72,13 +60,4 @@ final class HomeSlideFormSchema ->itemLabel(fn (array $state): string => filled($state['title'] ?? null) ? (string) $state['title'] : 'New Slide') ->dehydrateStateUsing(fn ($state) => $normalizeSlides($state)); } - - private static function mediaDriver(Get $get): string - { - $driver = $get('../../media_disk'); - - return is_string($driver) && trim($driver) !== '' - ? MediaStorage::normalizeDriver($driver) - : MediaStorage::activeDriver(); - } } diff --git a/Modules/Listing/Models/Listing.php b/Modules/Listing/Models/Listing.php index 977d9c77b..0a512ee5d 100644 --- a/Modules/Listing/Models/Listing.php +++ b/Modules/Listing/Models/Listing.php @@ -11,9 +11,11 @@ use Illuminate\Support\Carbon; use Illuminate\Support\Collection; use Illuminate\Support\Str; use Modules\Category\Models\Category; +use Modules\Conversation\App\Models\Conversation; use Modules\Listing\States\ListingStatus; use Modules\Listing\Support\ListingImageViewData; use Modules\Listing\Support\ListingPanelHelper; +use Modules\Site\App\Support\LocalMedia; use Modules\User\App\Models\User; use Modules\Video\Enums\VideoStatus; use Modules\Video\Models\Video; @@ -63,23 +65,23 @@ class Listing extends Model implements HasMedia public function category() { - return $this->belongsTo(\Modules\Category\Models\Category::class); + return $this->belongsTo(Category::class); } public function user() { - return $this->belongsTo(\Modules\User\App\Models\User::class); + return $this->belongsTo(User::class); } public function favoritedByUsers() { - return $this->belongsToMany(\Modules\User\App\Models\User::class, 'favorite_listings') + return $this->belongsToMany(User::class, 'favorite_listings') ->withTimestamps(); } public function conversations() { - return $this->hasMany(\Modules\Conversation\App\Models\Conversation::class); + return $this->hasMany(Conversation::class); } public function videos() @@ -453,6 +455,7 @@ class Listing extends Model implements HasMedia return; } + $disk = $this->mediaDisk(); $targetFileName = trim((string) ($fileName ?: basename($absolutePath))); $existingMediaItems = $this->getMedia('listing-images'); @@ -462,7 +465,7 @@ class Listing extends Model implements HasMedia if ( $existingMedia && (string) $existingMedia->file_name === $targetFileName - && (string) $existingMedia->disk === 'public' + && (string) $existingMedia->disk === $disk ) { try { if (is_file($existingMedia->getPath())) { @@ -474,12 +477,25 @@ class Listing extends Model implements HasMedia } $this->clearMediaCollection('listing-images'); + $this->attachListingImage($absolutePath, $targetFileName, $disk); + } + + public function attachListingImage(string $absolutePath, string $fileName, ?string $disk = null): void + { + if (! is_file($absolutePath)) { + return; + } + + $targetDisk = is_string($disk) && trim($disk) !== '' + ? trim($disk) + : $this->mediaDisk(); $this ->addMedia($absolutePath) - ->usingFileName($targetFileName) + ->usingFileName(trim($fileName)) + ->withCustomProperties(self::mediaCustomProperties()) ->preservingOriginal() - ->toMediaCollection('listing-images', 'public'); + ->toMediaCollection('listing-images', $targetDisk); } public function statusValue(): string @@ -571,7 +587,7 @@ class Listing extends Model implements HasMedia public function registerMediaCollections(): void { - $this->addMediaCollection('listing-images')->useDisk('public'); + $this->addMediaCollection('listing-images')->useDisk($this->mediaDisk()); } public function registerMediaConversions(?Media $media = null): void @@ -644,6 +660,37 @@ class Listing extends Model implements HasMedia return str_contains($argv, 'db:seed') || str_contains($argv, '--seed'); } + private function mediaDisk(): string + { + return LocalMedia::disk(); + } + + public static function mediaCustomProperties(): array + { + $scope = static::mediaPathScope(); + + return $scope !== null + ? ['path_scope' => $scope] + : []; + } + + public static function mediaPathScope(): ?string + { + $connection = (string) config('database.default', 'pgsql'); + $searchPath = config("database.connections.{$connection}.search_path"); + $value = is_array($searchPath) + ? implode('_', $searchPath) + : (string) $searchPath; + $scope = (string) Str::of($value) + ->before(',') + ->trim() + ->lower() + ->replaceMatches('/[^a-z0-9_]+/', '_') + ->trim('_'); + + return $scope !== '' ? $scope : null; + } + protected function location(): Attribute { return Attribute::make( diff --git a/Modules/Listing/Support/Filament/AdminListingResourceSchema.php b/Modules/Listing/Support/Filament/AdminListingResourceSchema.php index 1bad5c693..e66d9526f 100644 --- a/Modules/Listing/Support/Filament/AdminListingResourceSchema.php +++ b/Modules/Listing/Support/Filament/AdminListingResourceSchema.php @@ -23,6 +23,7 @@ use Filament\Tables\Filters\SelectFilter; use Filament\Tables\Filters\TernaryFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Str; use Modules\Admin\Support\Filament\ResourceTableActions; use Modules\Category\Models\Category; use Modules\Listing\Models\Listing; @@ -31,6 +32,7 @@ use Modules\Listing\Support\ListingPanelHelper; use Modules\Location\Models\City; use Modules\Location\Models\Country; use Modules\Location\Support\CountryCodeManager; +use Modules\Site\App\Support\LocalMedia; use Modules\Video\Support\Filament\VideoFormSchema; use Ysfkaya\FilamentPhoneInput\Forms\PhoneInput; @@ -39,7 +41,7 @@ class AdminListingResourceSchema public static function form(): array { return [ - TextInput::make('title')->required()->maxLength(255)->live(onBlur: true)->afterStateUpdated(fn ($state, $set) => $set('slug', \Illuminate\Support\Str::slug($state).'-'.\Illuminate\Support\Str::random(4))), + TextInput::make('title')->required()->maxLength(255)->live(onBlur: true)->afterStateUpdated(fn ($state, $set) => $set('slug', Str::slug($state).'-'.Str::random(4))), TextInput::make('slug')->required()->maxLength(255)->unique(ignoreRecord: true), Textarea::make('description')->rows(4), TextInput::make('price') @@ -101,6 +103,8 @@ class AdminListingResourceSchema ->columnSpanFull(), SpatieMediaLibraryFileUpload::make('images') ->collection('listing-images') + ->disk(fn (): string => LocalMedia::disk()) + ->customProperties(fn (): array => Listing::mediaCustomProperties()) ->multiple() ->image() ->reorderable(), diff --git a/Modules/Panel/App/Livewire/PanelQuickListingForm.php b/Modules/Panel/App/Livewire/PanelQuickListingForm.php index 12c0ec6ad..b243a49f5 100644 --- a/Modules/Panel/App/Livewire/PanelQuickListingForm.php +++ b/Modules/Panel/App/Livewire/PanelQuickListingForm.php @@ -18,7 +18,7 @@ use Modules\Listing\Support\ListingPanelHelper; use Modules\Listing\Support\QuickListingCategorySuggester; use Modules\Location\Models\City; use Modules\Location\Models\Country; -use Modules\S3\Support\MediaStorage; +use Modules\Site\App\Support\LocalMedia; use Modules\User\App\Models\Profile; use Modules\Video\Models\Video; use Throwable; @@ -641,10 +641,11 @@ class PanelQuickListingForm extends Component continue; } - $listing - ->addMedia($photo->getRealPath()) - ->usingFileName($photo->getClientOriginalName()) - ->toMediaCollection('listing-images', $mediaDisk); + $listing->attachListingImage( + $photo->getRealPath(), + $photo->getClientOriginalName(), + $mediaDisk + ); } foreach ($this->videos as $index => $video) { @@ -757,7 +758,7 @@ class PanelQuickListingForm extends Component private function frontendMediaDisk(): string { - return (string) config('media_storage.local_disk', MediaStorage::diskFromDriver(MediaStorage::DRIVER_LOCAL)); + return LocalMedia::disk(); } private function handlePublishValidationFailure(ValidationException $exception): void diff --git a/Modules/S3/Providers/S3ServiceProvider.php b/Modules/S3/Providers/S3ServiceProvider.php deleted file mode 100644 index 4861ed93d..000000000 --- a/Modules/S3/Providers/S3ServiceProvider.php +++ /dev/null @@ -1,13 +0,0 @@ -mergeConfigFrom(module_path('S3', 'config/s3.php'), 'media_storage'); - } -} diff --git a/Modules/S3/Support/MediaStorage.php b/Modules/S3/Support/MediaStorage.php deleted file mode 100644 index ca28882fb..000000000 --- a/Modules/S3/Support/MediaStorage.php +++ /dev/null @@ -1,145 +0,0 @@ - 'S3 Object Storage', - self::DRIVER_LOCAL => 'Local Storage', - ]; - } - - public static function defaultDriver(): string - { - return self::coerceDriver(config('media_storage.default_driver')) - ?? self::DRIVER_S3; - } - - public static function activeDriver(): string - { - if (! self::hasSettingsTable()) { - return self::defaultDriver(); - } - - try { - return self::normalizeDriver(app(GeneralSettings::class)->media_disk ?? null); - } catch (Throwable) { - return self::defaultDriver(); - } - } - - public static function normalizeDriver(mixed $driver): string - { - return self::coerceDriver($driver) ?? self::defaultDriver(); - } - - public static function diskFromDriver(mixed $driver = null): string - { - return self::normalizeDriver($driver) === self::DRIVER_LOCAL - ? (string) config('media_storage.local_disk', 'public') - : (string) config('media_storage.cloud_disk', 's3'); - } - - public static function activeDisk(): string - { - return self::diskFromDriver(self::activeDriver()); - } - - public static function storedDisk(mixed $disk = null, mixed $driver = null): string - { - if (is_string($disk) && trim($disk) !== '') { - return self::diskFromDriver(trim($disk) === 'public' ? self::DRIVER_LOCAL : trim($disk)); - } - - return self::diskFromDriver($driver); - } - - public static function managesPath(mixed $path): bool - { - $path = is_string($path) ? trim($path) : ''; - - if ($path === '') { - return false; - } - - return ! self::isExternalUrl($path) && ! self::isAssetPath($path); - } - - public static function url(mixed $path, mixed $disk = null): ?string - { - $path = is_string($path) ? trim($path) : ''; - - if ($path === '') { - return null; - } - - if (self::isExternalUrl($path)) { - return $path; - } - - if (self::isAssetPath($path)) { - return asset($path); - } - - return Storage::disk(self::storedDisk($disk))->url($path); - } - - public static function applyRuntimeConfig(): void - { - $disk = self::activeDisk(); - - config([ - 'filesystems.default' => $disk, - 'filemanager.disk' => $disk, - 'filament.default_filesystem_disk' => $disk, - 'media-library.disk_name' => $disk, - 'video.disk' => $disk, - ]); - } - - private static function coerceDriver(mixed $driver): ?string - { - if (! is_string($driver)) { - return null; - } - - return match (strtolower(trim($driver))) { - self::DRIVER_LOCAL, 'public' => self::DRIVER_LOCAL, - self::DRIVER_S3 => self::DRIVER_S3, - default => null, - }; - } - - private static function hasSettingsTable(): bool - { - try { - return Schema::hasTable('settings'); - } catch (Throwable) { - return false; - } - } - - private static function isAssetPath(string $path): bool - { - return str_starts_with($path, 'images/'); - } - - private static function isExternalUrl(string $path): bool - { - return str_starts_with($path, 'http://') - || str_starts_with($path, 'https://') - || str_starts_with($path, '//'); - } -} diff --git a/Modules/S3/config/s3.php b/Modules/S3/config/s3.php deleted file mode 100644 index c1be9e993..000000000 --- a/Modules/S3/config/s3.php +++ /dev/null @@ -1,7 +0,0 @@ - env('MEDIA_DISK', env('FILESYSTEM_DISK', 's3')), - 'local_disk' => env('LOCAL_MEDIA_DISK', 'public'), - 'cloud_disk' => env('CLOUD_MEDIA_DISK', 's3'), -]; diff --git a/Modules/S3/module.json b/Modules/S3/module.json deleted file mode 100644 index 7008b0870..000000000 --- a/Modules/S3/module.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "S3", - "alias": "s3", - "description": "Media storage selection and S3 object storage integration", - "keywords": [], - "priority": 0, - "providers": [ - "Modules\\S3\\Providers\\S3ServiceProvider" - ], - "aliases": {}, - "files": [] -} diff --git a/Modules/Site/App/Providers/SiteServiceProvider.php b/Modules/Site/App/Providers/SiteServiceProvider.php index bc2f2aca8..0d75cd07a 100644 --- a/Modules/Site/App/Providers/SiteServiceProvider.php +++ b/Modules/Site/App/Providers/SiteServiceProvider.php @@ -4,9 +4,24 @@ namespace Modules\Site\App\Providers; use Illuminate\Support\Facades\View; use Illuminate\Support\ServiceProvider; +use Modules\Site\App\Support\LocalMedia; +use Modules\Site\App\Support\ScopedMediaPathGenerator; class SiteServiceProvider extends ServiceProvider { + public function register(): void + { + config([ + 'filesystems.default' => LocalMedia::disk(), + 'filament.default_filesystem_disk' => LocalMedia::disk(), + 'filemanager.storage_mode.disk' => LocalMedia::disk(), + 'filemanager.upload.disk' => LocalMedia::disk(), + 'media-library.disk_name' => LocalMedia::disk(), + 'media-library.path_generator' => ScopedMediaPathGenerator::class, + 'video.disk' => LocalMedia::disk(), + ]); + } + public function boot(): void { $viewPath = module_path('Site', 'resources/views'); diff --git a/Modules/Site/App/Settings/GeneralSettings.php b/Modules/Site/App/Settings/GeneralSettings.php index bd00a4c45..f5fb5c8f2 100644 --- a/Modules/Site/App/Settings/GeneralSettings.php +++ b/Modules/Site/App/Settings/GeneralSettings.php @@ -10,12 +10,8 @@ class GeneralSettings extends Settings public string $site_description; - public string $media_disk; - public ?string $site_logo; - public ?string $site_logo_disk; - public string $default_language; public string $default_country_code; diff --git a/Modules/Site/App/Support/HomeSlideDefaults.php b/Modules/Site/App/Support/HomeSlideDefaults.php index 02ea33adf..e621c76d2 100644 --- a/Modules/Site/App/Support/HomeSlideDefaults.php +++ b/Modules/Site/App/Support/HomeSlideDefaults.php @@ -3,7 +3,6 @@ namespace Modules\Site\App\Support; use Illuminate\Support\Arr; -use Modules\S3\Support\MediaStorage; final class HomeSlideDefaults { @@ -17,7 +16,6 @@ final class HomeSlideDefaults 'primary_button_text' => 'Browse Listings', 'secondary_button_text' => 'Post Listing', 'image_path' => 'images/home-slides/slide-marketplace.svg', - 'disk' => null, ], [ 'badge' => 'Fresh Categories', @@ -26,7 +24,6 @@ final class HomeSlideDefaults 'primary_button_text' => 'See Categories', 'secondary_button_text' => 'Start Now', 'image_path' => 'images/home-slides/slide-categories.svg', - 'disk' => null, ], [ 'badge' => 'Local Shopping', @@ -35,12 +32,11 @@ final class HomeSlideDefaults 'primary_button_text' => 'Nearby Deals', 'secondary_button_text' => 'Sell for Free', 'image_path' => 'images/home-slides/slide-local.svg', - 'disk' => null, ], ]; } - public static function normalize(mixed $slides, ?string $defaultDisk = null): array + public static function normalize(mixed $slides): array { $defaults = self::defaults(); $source = is_array($slides) ? $slides : []; @@ -48,7 +44,7 @@ final class HomeSlideDefaults $normalized = collect($source) ->filter(fn ($slide): bool => is_array($slide)) ->values() - ->map(function (array $slide, int $index) use ($defaults, $defaultDisk): ?array { + ->map(function (array $slide, int $index) use ($defaults): ?array { $fallback = $defaults[$index] ?? $defaults[array_key_last($defaults)]; $badge = trim((string) ($slide['badge'] ?? '')); $title = trim((string) ($slide['title'] ?? '')); @@ -56,9 +52,6 @@ final class HomeSlideDefaults $primaryButtonText = trim((string) ($slide['primary_button_text'] ?? '')); $secondaryButtonText = trim((string) ($slide['secondary_button_text'] ?? '')); $imagePath = self::normalizeImagePath($slide['image_path'] ?? null); - $disk = MediaStorage::managesPath($imagePath) - ? MediaStorage::storedDisk($slide['disk'] ?? null, $defaultDisk) - : null; if ($title === '') { return null; @@ -71,7 +64,6 @@ final class HomeSlideDefaults 'primary_button_text' => $primaryButtonText !== '' ? $primaryButtonText : $fallback['primary_button_text'], 'secondary_button_text' => $secondaryButtonText !== '' ? $secondaryButtonText : $fallback['secondary_button_text'], 'image_path' => $imagePath !== '' ? $imagePath : ($fallback['image_path'] ?? null), - 'disk' => $imagePath !== '' ? $disk : ($fallback['disk'] ?? null), ]; }) ->filter(fn ($slide): bool => is_array($slide)) diff --git a/Modules/Site/App/Support/LocalMedia.php b/Modules/Site/App/Support/LocalMedia.php new file mode 100644 index 000000000..f7ccd3e4e --- /dev/null +++ b/Modules/Site/App/Support/LocalMedia.php @@ -0,0 +1,55 @@ +url($value); + } + + private static function isAssetPath(string $path): bool + { + return str_starts_with($path, 'images/'); + } + + private static function isExternalUrl(string $path): bool + { + return str_starts_with($path, 'http://') + || str_starts_with($path, 'https://') + || str_starts_with($path, '//'); + } +} diff --git a/Modules/Site/App/Support/RequestAppData.php b/Modules/Site/App/Support/RequestAppData.php index c260e6e93..48fea4597 100644 --- a/Modules/Site/App/Support/RequestAppData.php +++ b/Modules/Site/App/Support/RequestAppData.php @@ -8,7 +8,6 @@ use Illuminate\Support\Facades\View; use Modules\Category\Models\Category; use Modules\Location\Models\Country; use Modules\Location\Support\CountryCodeManager; -use Modules\S3\Support\MediaStorage; use Modules\Site\App\Settings\GeneralSettings; use Modules\User\App\Models\User; use Throwable; @@ -42,12 +41,10 @@ final class RequestAppData $fallbackAppleClientId = config('services.apple.client_id'); $fallbackAppleClientSecret = config('services.apple.client_secret'); $fallbackDefaultCountryCode = (string) config('app.default_country_code', '+90'); - $fallbackMediaDriver = MediaStorage::defaultDriver(); $generalSettings = [ 'site_name' => $fallbackName, 'site_description' => $fallbackDescription, - 'media_disk' => $fallbackMediaDriver, 'home_slides' => $fallbackHomeSlides, 'site_logo_url' => null, 'default_language' => $fallbackLocale, @@ -94,18 +91,13 @@ final class RequestAppData $appleClientId = trim((string) ($settings->apple_client_id ?: $fallbackAppleClientId)); $appleClientSecret = trim((string) ($settings->apple_client_secret ?: $fallbackAppleClientSecret)); $defaultCountryCode = CountryCodeManager::normalizeCountryCode($settings->default_country_code ?? $fallbackDefaultCountryCode); - $mediaDriver = MediaStorage::normalizeDriver($settings->media_disk ?? null); return [ 'site_name' => trim((string) ($settings->site_name ?: $fallbackName)), 'site_description' => trim((string) ($settings->site_description ?: $fallbackDescription)), - 'media_disk' => $mediaDriver, - 'home_slides' => HomeSlideDefaults::normalize( - $settings->home_slides ?? [], - MediaStorage::diskFromDriver($mediaDriver), - ), + 'home_slides' => HomeSlideDefaults::normalize($settings->home_slides ?? []), 'site_logo_url' => filled($settings->site_logo) - ? MediaStorage::url($settings->site_logo, $settings->site_logo_disk ?? null) + ? LocalMedia::url($settings->site_logo) : null, 'default_language' => $defaultLanguage, 'default_country_code' => $defaultCountryCode, @@ -164,8 +156,6 @@ final class RequestAppData 'app.default_country_code' => $generalSettings['default_country_code'] ?? '+90', 'app.default_country_iso2' => CountryCodeManager::iso2FromCountryCode($generalSettings['default_country_code'] ?? '+90') ?? 'TR', ]); - - MediaStorage::applyRuntimeConfig(); } private function resolveHeaderLocationCountries(): array diff --git a/Modules/Site/App/Support/ScopedMediaPathGenerator.php b/Modules/Site/App/Support/ScopedMediaPathGenerator.php new file mode 100644 index 000000000..1726b1495 --- /dev/null +++ b/Modules/Site/App/Support/ScopedMediaPathGenerator.php @@ -0,0 +1,34 @@ +basePath($media).'/'; + } + + public function getPathForConversions(Media $media): string + { + return $this->basePath($media).'/conversions/'; + } + + public function getPathForResponsiveImages(Media $media): string + { + return $this->basePath($media).'/responsive-images/'; + } + + private function basePath(Media $media): string + { + $scope = trim((string) $media->getCustomProperty('path_scope', '')); + $key = (string) $media->getKey(); + + return $scope !== '' + ? $scope.'/'.$key + : $key; + } +} diff --git a/Modules/Site/resources/views/home.blade.php b/Modules/Site/resources/views/home.blade.php index 3d19d862b..fb1bf1999 100644 --- a/Modules/Site/resources/views/home.blade.php +++ b/Modules/Site/resources/views/home.blade.php @@ -44,7 +44,7 @@ 'subtitle' => $subtitle !== '' ? $subtitle : 'Buy and sell everything in your area', 'primary_button_text' => $primaryButtonText !== '' ? $primaryButtonText : 'Browse Listings', 'secondary_button_text' => $secondaryButtonText !== '' ? $secondaryButtonText : 'Post Listing', - 'image_url' => \Modules\S3\Support\MediaStorage::url($imagePath, $slide['disk'] ?? null), + 'image_url' => \Modules\Site\App\Support\LocalMedia::url($imagePath), ]; }) ->values(); diff --git a/Modules/User/App/Models/User.php b/Modules/User/App/Models/User.php index 2c9c60c5d..2e0e08cfb 100644 --- a/Modules/User/App/Models/User.php +++ b/Modules/User/App/Models/User.php @@ -2,6 +2,7 @@ namespace Modules\User\App\Models; +use Database\Factories\UserFactory; use Filament\Models\Contracts\FilamentUser; use Filament\Models\Contracts\HasAvatar; use Filament\Panel; @@ -17,7 +18,7 @@ use Modules\Conversation\App\Models\Conversation; use Modules\Conversation\App\Models\ConversationMessage; use Modules\Favorite\App\Models\FavoriteSearch; use Modules\Listing\Models\Listing; -use Modules\S3\Support\MediaStorage; +use Modules\Site\App\Support\LocalMedia; use Modules\User\App\States\UserStatus; use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; @@ -50,7 +51,7 @@ class User extends Authenticatable implements FilamentUser, HasAvatar protected static function newFactory(): Factory { - return \Database\Factories\UserFactory::new(); + return UserFactory::new(); } public function getActivitylogOptions(): LogOptions @@ -128,21 +129,17 @@ class User extends Authenticatable implements FilamentUser, HasAvatar $path = trim((string) $this->avatar_url); - if (! MediaStorage::managesPath($path)) { - return MediaStorage::url($path); + if (! LocalMedia::managesPath($path)) { + return LocalMedia::url($path); } - $activeDisk = MediaStorage::activeDisk(); + $disk = LocalMedia::disk(); - if (Storage::disk($activeDisk)->exists($path)) { - return Storage::disk($activeDisk)->url($path); + if (Storage::disk($disk)->exists($path)) { + return Storage::disk($disk)->url($path); } - if ($activeDisk !== 'public' && Storage::disk('public')->exists($path)) { - return Storage::disk('public')->url($path); - } - - return MediaStorage::url($path, $activeDisk); + return LocalMedia::url($path); } public function getDisplayName(): string diff --git a/Modules/Video/Models/Video.php b/Modules/Video/Models/Video.php index 2d0f1d74c..ff98208ae 100644 --- a/Modules/Video/Models/Video.php +++ b/Modules/Video/Models/Video.php @@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; use Modules\Listing\Models\Listing; -use Modules\S3\Support\MediaStorage; +use Modules\Site\App\Support\LocalMedia; use Modules\User\App\Models\User; use Modules\Video\Enums\VideoStatus; use Modules\Video\Jobs\ProcessVideo; @@ -94,7 +94,7 @@ class Video extends Model public static function createFromTemporaryUpload(Listing $listing, TemporaryUploadedFile $file, array $attributes = []): self { - $disk = (string) ($attributes['disk'] ?? config('video.disk', MediaStorage::activeDisk())); + $disk = (string) ($attributes['disk'] ?? config('video.disk', LocalMedia::disk())); $path = $file->storeAs( trim((string) config('video.upload_directory', 'videos/uploads').'/'.$listing->getKey(), '/'), Str::ulid().'.'.($file->getClientOriginalExtension() ?: $file->guessExtension() ?: 'mp4'), @@ -117,7 +117,7 @@ class Video extends Model public static function createFromUploadedFile(Listing $listing, UploadedFile $file, array $attributes = []): self { - $disk = (string) ($attributes['disk'] ?? config('video.disk', MediaStorage::activeDisk())); + $disk = (string) ($attributes['disk'] ?? config('video.disk', LocalMedia::disk())); $path = $file->storeAs( trim((string) config('video.upload_directory', 'videos/uploads').'/'.$listing->getKey(), '/'), Str::ulid().'.'.($file->getClientOriginalExtension() ?: $file->extension() ?: 'mp4'), @@ -176,9 +176,9 @@ class Video extends Model $uploadPath = $this->upload_path; $this->forceFill([ - 'disk' => $attributes['disk'] ?? (string) config('video.disk', MediaStorage::activeDisk()), + 'disk' => $attributes['disk'] ?? (string) config('video.disk', LocalMedia::disk()), 'path' => $attributes['path'] ?? null, - 'upload_disk' => (string) config('video.disk', MediaStorage::activeDisk()), + 'upload_disk' => (string) config('video.disk', LocalMedia::disk()), 'upload_path' => null, 'mime_type' => $attributes['mime_type'] ?? 'video/mp4', 'size' => $attributes['size'] ?? null, @@ -227,14 +227,14 @@ class Video extends Model $status = $this->currentStatus(); if (($status !== VideoStatus::Ready) && filled($this->upload_path)) { - return (string) ($this->upload_disk ?: config('video.disk', MediaStorage::activeDisk())); + return (string) ($this->upload_disk ?: config('video.disk', LocalMedia::disk())); } if (filled($this->path)) { - return (string) ($this->disk ?: config('video.disk', MediaStorage::activeDisk())); + return (string) ($this->disk ?: config('video.disk', LocalMedia::disk())); } - return (string) ($this->upload_disk ?: config('video.disk', MediaStorage::activeDisk())); + return (string) ($this->upload_disk ?: config('video.disk', LocalMedia::disk())); } public function playableUrl(): ?string @@ -355,7 +355,7 @@ class Video extends Model $this->previousUploadDisk = filled($this->getOriginal('upload_disk')) ? (string) $this->getOriginal('upload_disk') - : (string) config('video.disk', MediaStorage::activeDisk()); + : (string) config('video.disk', LocalMedia::disk()); $this->previousUploadPath = filled($this->getOriginal('upload_path')) ? (string) $this->getOriginal('upload_path') @@ -380,11 +380,11 @@ class Video extends Model protected function normalizeStatus(): void { if (blank($this->disk)) { - $this->disk = (string) config('video.disk', MediaStorage::activeDisk()); + $this->disk = (string) config('video.disk', LocalMedia::disk()); } if (blank($this->upload_disk)) { - $this->upload_disk = (string) config('video.disk', MediaStorage::activeDisk()); + $this->upload_disk = (string) config('video.disk', LocalMedia::disk()); } if (! $this->isDirty('upload_path')) { @@ -452,7 +452,7 @@ class Video extends Model protected function replaceUploadFromUploadedFile(UploadedFile $file): void { - $disk = (string) config('video.disk', MediaStorage::activeDisk()); + $disk = (string) config('video.disk', LocalMedia::disk()); $path = $file->storeAs( trim((string) config('video.upload_directory', 'videos/uploads').'/'.$this->listing_id, '/'), Str::ulid().'.'.($file->getClientOriginalExtension() ?: $file->extension() ?: 'mp4'), diff --git a/Modules/Video/Support/Filament/VideoFormSchema.php b/Modules/Video/Support/Filament/VideoFormSchema.php index b527c7177..e862c798b 100644 --- a/Modules/Video/Support/Filament/VideoFormSchema.php +++ b/Modules/Video/Support/Filament/VideoFormSchema.php @@ -15,7 +15,7 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\HtmlString; use Illuminate\Support\Str; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; -use Modules\S3\Support\MediaStorage; +use Modules\Site\App\Support\LocalMedia; use Modules\Video\Models\Video; class VideoFormSchema @@ -127,7 +127,7 @@ class VideoFormSchema return FileUpload::make('upload_path') ->label('Source video') - ->disk(fn (?Video $record): string => MediaStorage::storedDisk($record?->upload_disk)) + ->disk(fn (): string => LocalMedia::disk()) ->directory(trim((string) config('video.upload_directory', 'videos/uploads'), '/')) ->visibility('public') ->acceptedFileTypes([ @@ -192,7 +192,7 @@ class VideoFormSchema protected static function normalizeData(array $data): array { - $data['upload_disk'] = (string) config('video.disk', MediaStorage::activeDisk()); + $data['upload_disk'] = (string) config('video.disk', LocalMedia::disk()); if (blank($data['title'] ?? null) && filled($data['upload_path'] ?? null)) { $data['title'] = str(pathinfo((string) $data['upload_path'], PATHINFO_FILENAME)) diff --git a/Modules/Video/Support/VideoTranscoder.php b/Modules/Video/Support/VideoTranscoder.php index 3d9096e2b..372d9c77d 100644 --- a/Modules/Video/Support/VideoTranscoder.php +++ b/Modules/Video/Support/VideoTranscoder.php @@ -4,7 +4,7 @@ namespace Modules\Video\Support; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; -use Modules\S3\Support\MediaStorage; +use Modules\Site\App\Support\LocalMedia; use Modules\Video\Models\Video; use RuntimeException; use Symfony\Component\Process\Process; @@ -13,7 +13,7 @@ class VideoTranscoder { public function transcode(Video $video): array { - $disk = (string) config('video.disk', MediaStorage::activeDisk()); + $disk = (string) config('video.disk', LocalMedia::disk()); $inputDisk = Storage::disk((string) ($video->upload_disk ?: $disk)); $outputDisk = Storage::disk($disk); $workspace = storage_path('app/private/video-processing/'.Str::uuid()); diff --git a/Modules/Video/config/video.php b/Modules/Video/config/video.php index 321eb68e8..367cacd26 100644 --- a/Modules/Video/config/video.php +++ b/Modules/Video/config/video.php @@ -1,7 +1,7 @@ env('VIDEO_DISK', env('MEDIA_DISK', env('FILESYSTEM_DISK', 's3'))), + 'disk' => env('VIDEO_DISK', env('FILESYSTEM_DISK', 'public')), 'upload_directory' => env('VIDEO_UPLOAD_DIRECTORY', 'videos/uploads'), 'processed_directory' => env('VIDEO_PROCESSED_DIRECTORY', 'videos/mobile'), 'queue' => env('VIDEO_QUEUE', 'videos'), diff --git a/composer.json b/composer.json index 3e5731e3b..a81c20ea6 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,6 @@ "php": "^8.2", "a909m/filament-statefusion": "^2.3", "ariaieboy/filament-currency": "^3.0", - "aws/aws-sdk-php": "^3.322", "bezhansalleh/filament-language-switch": "^4.1", "cheesegrits/filament-google-maps": "^5.0", "dutchcodingcompany/filament-developer-logins": "^2.1", @@ -23,7 +22,6 @@ "laravel/reverb": "^1.8", "laravel/sanctum": "^4.3", "laravel/tinker": "^2.10.1", - "league/flysystem-aws-s3-v3": "^3.25", "mwguerra/filemanager": "^2.0", "nwidart/laravel-modules": "^11.0", "pxlrbt/filament-activity-log": "^2.1", diff --git a/config/filemanager.php b/config/filemanager.php index 1fd2788a4..f0d5a525a 100644 --- a/config/filemanager.php +++ b/config/filemanager.php @@ -1,9 +1,12 @@ 'database', // 'database' or 'storage' 'storage_mode' => [ - 'disk' => env('FILEMANAGER_DISK', env('FILESYSTEM_DISK', 'public')), + 'disk' => 'public', 'root' => env('FILEMANAGER_ROOT', ''), 'show_hidden' => env('FILEMANAGER_SHOW_HIDDEN', false), 'url_expiration' => env('FILEMANAGER_URL_EXPIRATION', 60), @@ -17,7 +20,7 @@ return [ 'public_disks' => ['public'], 'public_access_disks' => [], ], - 'model' => \MWGuerra\FileManager\Models\FileSystemItem::class, + 'model' => FileSystemItem::class, 'file_manager' => [ 'enabled' => true, 'navigation' => [ @@ -40,7 +43,7 @@ return [ 'enabled' => true, ], 'upload' => [ - 'disk' => env('FILEMANAGER_DISK', env('FILESYSTEM_DISK', 'public')), + 'disk' => 'public', 'directory' => env('FILEMANAGER_UPLOAD_DIR', 'uploads'), 'max_file_size' => 100 * 1024, // 100 MB in kilobytes 'allowed_mimes' => [ @@ -91,7 +94,7 @@ return [ 'delete_any' => null, // Bulk delete 'download' => null, // Download files ], - 'policy' => \MWGuerra\FileManager\Policies\FileSystemItemPolicy::class, + 'policy' => FileSystemItemPolicy::class, ], 'sidebar' => [ 'enabled' => true, diff --git a/config/filesystems.php b/config/filesystems.php index 637cdfbc4..ef7a1d802 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -1,7 +1,7 @@ env('FILESYSTEM_DISK', env('MEDIA_DISK', 's3')), + 'default' => env('FILESYSTEM_DISK', 'public'), 'disks' => [ 'local' => [ @@ -21,20 +21,6 @@ return [ 'report' => false, ], - 's3' => [ - 'driver' => 's3', - 'key' => env('AWS_ACCESS_KEY_ID', env('OBJECT_STORAGE_ACCESS_KEY_ID')), - 'secret' => env('AWS_SECRET_ACCESS_KEY', env('OBJECT_STORAGE_SECRET_ACCESS_KEY')), - 'region' => env('AWS_DEFAULT_REGION', env('OBJECT_STORAGE_REGION', 'hel1')), - 'bucket' => env('AWS_BUCKET', env('OBJECT_STORAGE_BUCKET')), - 'url' => env('AWS_URL', env('OBJECT_STORAGE_URL')), - 'endpoint' => env('AWS_ENDPOINT', env('OBJECT_STORAGE_ENDPOINT')), - 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), - 'visibility' => 'public', - 'throw' => false, - 'report' => false, - ], - ], 'links' => [ public_path('storage') => storage_path('app/public'), diff --git a/database/settings/2026_03_03_120000_create_general_settings.php b/database/settings/2026_03_03_120000_create_general_settings.php index 59405f97e..2f35072e5 100644 --- a/database/settings/2026_03_03_120000_create_general_settings.php +++ b/database/settings/2026_03_03_120000_create_general_settings.php @@ -1,5 +1,6 @@ migrator->add('general.site_name', 'OpenClassify'); $this->migrator->add('general.site_description', 'The marketplace for buying and selling everything.'); - $this->migrator->add('general.media_disk', \Modules\S3\Support\MediaStorage::defaultDriver()); $this->migrator->add('general.site_logo', null); - $this->migrator->add('general.site_logo_disk', null); $this->migrator->add('general.default_language', 'en'); $this->migrator->add('general.default_country_code', '+90'); $this->migrator->add('general.currencies', ['USD']); @@ -30,6 +29,6 @@ return new class extends SettingsMigration $this->migrator->add('general.enable_apple_login', false); $this->migrator->add('general.apple_client_id', null); $this->migrator->add('general.apple_client_secret', null); - $this->migrator->add('general.home_slides', \Modules\Site\App\Support\HomeSlideDefaults::defaults()); + $this->migrator->add('general.home_slides', HomeSlideDefaults::defaults()); } }; diff --git a/modules_statuses.json b/modules_statuses.json index 1e51aaf49..3d841c3aa 100644 --- a/modules_statuses.json +++ b/modules_statuses.json @@ -8,7 +8,6 @@ "Favorite": true, "User": true, "Video": true, - "S3": true, "Demo": true, "Panel": true, "Site": true