diff --git a/.env.development.example b/.env.development.example index 380f10a44..56c17128c 100644 --- a/.env.development.example +++ b/.env.development.example @@ -53,3 +53,4 @@ DUSK_DRIVER_URL=http://selenium:4444 BUNNY_API_KEY= # For asset uploads BUNNY_STORAGE_API_KEY= +AVATAR_CDN_URL= diff --git a/.env.windows-docker-desktop.example b/.env.windows-docker-desktop.example index b067b4c5c..626d76ff6 100644 --- a/.env.windows-docker-desktop.example +++ b/.env.windows-docker-desktop.example @@ -11,3 +11,4 @@ REDIS_PASSWORD=coolify PUSHER_APP_ID=coolify PUSHER_APP_KEY=coolify PUSHER_APP_SECRET=coolify +AVATAR_CDN_URL= diff --git a/app/Livewire/Profile/Index.php b/app/Livewire/Profile/Index.php index a20a1231b..04f58dadd 100644 --- a/app/Livewire/Profile/Index.php +++ b/app/Livewire/Profile/Index.php @@ -47,7 +47,7 @@ public function uploadAvatar(AvatarStorageService $avatarStorage): bool $avatarStorage->store(Auth::user(), $this->avatar); $this->reset('avatar'); - $this->dispatch('avatar-updated', url: route('profile.avatar', ['v' => Auth::user()->fresh()->updated_at->timestamp])); + $this->dispatch('avatar-updated', url: profile_avatar_url(Auth::user()->fresh())); $this->dispatch('success', 'Profile picture updated.'); return true; diff --git a/app/Livewire/Project/Index.php b/app/Livewire/Project/Index.php index 2b472a1a2..f43b7542e 100644 --- a/app/Livewire/Project/Index.php +++ b/app/Livewire/Project/Index.php @@ -53,10 +53,7 @@ public function render(): View 'uuid' => $project->uuid, 'name' => $project->name, 'description' => $project->description, - 'iconUrl' => $project->icon_path ? route('project.icon', [ - 'project_uuid' => $project->uuid, - 'v' => $project->updated_at->timestamp, - ]) : null, + 'iconUrl' => $project->icon_path ? project_icon_url($project) : null, 'href' => $project->navigateTo(), 'environmentCount' => $project->environments->count(), 'resourceCount' => $resourceCount, diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index c3bd4a223..f4dd3d185 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -12,6 +12,8 @@ use App\Models\InstanceSettings; use App\Models\LocalFileVolume; use App\Models\LocalPersistentVolume; +use App\Models\Project; +use App\Models\S3Storage; use App\Models\Server; use App\Models\Service; use App\Models\ServiceApplication; @@ -815,6 +817,54 @@ function firstDomainFromList(?string $fqdns): string { return trim((string) str($fqdns ?? '')->explode(',')->first()); } +function profile_avatar_url(User $user): string +{ + if ($user->avatar_storage_type === 's3') { + $url = s3_image_url($user->avatar_s3_storage_id, $user->avatar_path, $user->updated_at->timestamp); + if ($url) { + return $url; + } + } + + return route('profile.avatar', ['v' => $user->updated_at->timestamp]); +} + +function project_icon_url(Project $project): string +{ + if ($project->icon_storage_type === 's3') { + $url = s3_image_url($project->icon_s3_storage_id, $project->icon_path, $project->updated_at->timestamp); + if ($url) { + return $url; + } + } + + return route('project.icon', [ + 'project_uuid' => $project->uuid, + 'v' => $project->updated_at->timestamp, + ]); +} + +function s3_image_url(?int $storageId, ?string $path, int $version): ?string +{ + if (! $storageId || blank($path)) { + return null; + } + + $storage = S3Storage::query() + ->whereKey($storageId) + ->whereTeamId(0) + ->where('is_usable', true) + ->first(); + + if (! $storage) { + return null; + } + + $baseUrl = config('constants.coolify.avatar_cdn_url') ?: $storage->awsUrl(); + + return rtrim($baseUrl, '/').'/'.ltrim($path, '/').'?v='.$version; +} + /** * If fqdn is set, return it, otherwise return public ip. */ diff --git a/config/constants.php b/config/constants.php index b6fac21d1..e6e1a50f8 100644 --- a/config/constants.php +++ b/config/constants.php @@ -14,6 +14,7 @@ 'realtime_image' => env('REALTIME_IMAGE', env('REGISTRY_URL', 'docker.io').'/coollabsio/coolify-realtime'), 'is_windows_docker_desktop' => env('IS_WINDOWS_DOCKER_DESKTOP', false), 'cdn_url' => env('CDN_URL', 'https://cdn.coollabs.io'), + 'avatar_cdn_url' => env('AVATAR_CDN_URL'), 'versions_url' => env('VERSIONS_URL', env('CDN_URL', 'https://cdn.coollabs.io').'/coolify/versions.json'), 'upgrade_script_url' => env('UPGRADE_SCRIPT_URL', env('CDN_URL', 'https://cdn.coollabs.io').'/coolify/upgrade.sh'), 'releases_url' => env('RELEASES_URL', 'https://cdn.coollabs.io/coolify/releases.json'), diff --git a/resources/views/components/top-user-menu.blade.php b/resources/views/components/top-user-menu.blade.php index b176f4f21..7fdc312cb 100644 --- a/resources/views/components/top-user-menu.blade.php +++ b/resources/views/components/top-user-menu.blade.php @@ -15,7 +15,7 @@ pageWidth: localStorage.getItem('pageWidth') || 'full', themeColor: localStorage.getItem('themeColor') || '#6b16ed', themeColorFrame: null, - avatarUrl: @js($user?->avatar_path ? route('profile.avatar', ['v' => $user->updated_at->timestamp]) : null), + avatarUrl: @js($user?->avatar_path ? profile_avatar_url($user) : null), openPanel() { this.appearanceOpen = false; this.open = true; diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php index a8f4d0369..ebee5d2a3 100644 --- a/resources/views/livewire/dashboard.blade.php +++ b/resources/views/livewire/dashboard.blade.php @@ -66,7 +66,7 @@ class="absolute inset-0 rounded-xl"