refactor: improve avatar error handling, storage URL resolution, and upload logging
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m46s
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m46s
This commit is contained in:
parent
c899d7db13
commit
30c6fc5747
4 changed files with 78 additions and 7 deletions
|
|
@ -48,7 +48,16 @@ function uploadFromUrl(?string $url, string $directory = 'social-accounts'): ?st
|
|||
$extension
|
||||
);
|
||||
|
||||
Storage::put($filename, $response->body(), 'public');
|
||||
$stored = Storage::put($filename, $response->body(), 'public');
|
||||
|
||||
if (! $stored) {
|
||||
Log::warning('uploadFromUrl: Storage::put failed', [
|
||||
'url' => $url,
|
||||
'filename' => $filename,
|
||||
]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $filename;
|
||||
} catch (Exception $e) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
use App\Enums\SocialAccount\Platform as SocialPlatform;
|
||||
use Database\Factories\PostPlatformFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
|
@ -20,6 +21,10 @@ class PostPlatform extends Model
|
|||
/** @use HasFactory<PostPlatformFactory> */
|
||||
use HasFactory, HasUuids;
|
||||
|
||||
protected $appends = [
|
||||
'display_avatar',
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'post_id',
|
||||
'social_account_id',
|
||||
|
|
@ -117,7 +122,40 @@ public function getDisplayAvatarAttribute(): ?string
|
|||
return $this->socialAccount->avatar_url;
|
||||
}
|
||||
|
||||
return $this->platform_avatar ? Storage::url($this->platform_avatar) : null;
|
||||
if (! $this->platform_avatar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_starts_with($this->platform_avatar, 'http://') || str_starts_with($this->platform_avatar, 'https://')) {
|
||||
return $this->platform_avatar;
|
||||
}
|
||||
|
||||
if (Storage::disk('public')->exists($this->platform_avatar)) {
|
||||
return Storage::disk('public')->url($this->platform_avatar);
|
||||
}
|
||||
|
||||
return Storage::url($this->platform_avatar);
|
||||
}
|
||||
|
||||
protected function platformAvatar(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function (?string $value): ?string {
|
||||
if (! $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_starts_with($value, 'http://') || str_starts_with($value, 'https://')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (Storage::disk('public')->exists($value)) {
|
||||
return Storage::disk('public')->url($value);
|
||||
}
|
||||
|
||||
return Storage::url($value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
public function markAsPublishing(): void
|
||||
|
|
|
|||
|
|
@ -253,7 +253,21 @@ public function needsProactiveTokenRefresh(): bool
|
|||
protected function avatarUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (?string $value) => $value ? Storage::url($value) : null,
|
||||
get: function (?string $value): ?string {
|
||||
if (! $value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_starts_with($value, 'http://') || str_starts_with($value, 'https://')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (Storage::disk('public')->exists($value)) {
|
||||
return Storage::disk('public')->url($value);
|
||||
}
|
||||
|
||||
return Storage::url($value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from "vue"
|
||||
import { computed } from "vue"
|
||||
import { computed, ref, watch } from "vue"
|
||||
import { AvatarRoot } from "reka-ui"
|
||||
import { IconUser } from "@tabler/icons-vue"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { getInitials } from "@/composables/useInitials"
|
||||
|
||||
|
|
@ -12,7 +13,14 @@ const props = defineProps<{
|
|||
fallbackClass?: HTMLAttributes["class"]
|
||||
}>()
|
||||
|
||||
const hasError = ref(false)
|
||||
|
||||
watch(() => props.src, () => {
|
||||
hasError.value = false
|
||||
})
|
||||
|
||||
const hasProps = computed(() => props.src !== undefined || props.name !== undefined)
|
||||
const initials = computed(() => (props.name ? getInitials(props.name) : ''))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -22,16 +30,18 @@ const hasProps = computed(() => props.src !== undefined || props.name !== undefi
|
|||
>
|
||||
<template v-if="hasProps">
|
||||
<img
|
||||
v-if="src"
|
||||
v-if="src && !hasError"
|
||||
:src="src"
|
||||
:alt="name"
|
||||
class="aspect-square size-full object-cover"
|
||||
@error="hasError = true"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
:class="cn('flex size-full items-center justify-center bg-muted font-medium', props.fallbackClass)"
|
||||
:class="cn('flex size-full items-center justify-center bg-muted text-xs font-bold text-foreground select-none', props.fallbackClass)"
|
||||
>
|
||||
{{ name ? getInitials(name) : '' }}
|
||||
<span v-if="initials">{{ initials }}</span>
|
||||
<IconUser v-else class="size-1/2 opacity-70" />
|
||||
</div>
|
||||
</template>
|
||||
<slot v-else />
|
||||
|
|
|
|||
Loading…
Reference in a new issue