Implement creator studio and upload updates

This commit is contained in:
2026-04-04 10:12:02 +02:00
parent 1da7d3bf88
commit 0b216b7ecd
15107 changed files with 31206 additions and 626514 deletions

View File

@@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
namespace App\Services\Cdn;
use App\Services\ThumbnailService;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
final class ArtworkCdnPurgeService
{
/**
* @param array<int, string> $objectPaths
* @param array<string, mixed> $context
*/
public function purgeArtworkObjectPaths(array $objectPaths, array $context = []): bool
{
$urls = array_values(array_unique(array_filter(array_map(
fn (mixed $path): ?string => is_string($path) && trim($path) !== ''
? $this->cdnUrlForObjectPath($path)
: null,
$objectPaths,
))));
return $this->purgeUrls($urls, $context);
}
/**
* @param array<int, string> $variants
* @param array<string, mixed> $context
*/
public function purgeArtworkHashVariants(string $hash, string $extension = 'webp', array $variants = ['xs', 'sm', 'md', 'lg', 'xl', 'sq'], array $context = []): bool
{
$urls = array_values(array_unique(array_filter(array_map(
fn (string $variant): ?string => ThumbnailService::fromHash($hash, $extension, $variant),
$variants,
))));
return $this->purgeUrls($urls, $context + ['hash' => $hash]);
}
/**
* @param array<int, string> $urls
* @param array<string, mixed> $context
*/
private function purgeUrls(array $urls, array $context = []): bool
{
if ($urls === []) {
return false;
}
if ($this->hasCloudflareCredentials()) {
return $this->purgeViaCloudflare($urls, $context);
}
$legacyPurgeUrl = trim((string) config('cdn.purge_url', ''));
if ($legacyPurgeUrl !== '') {
return $this->purgeViaLegacyWebhook($legacyPurgeUrl, $urls, $context);
}
Log::debug('CDN purge skipped - no Cloudflare or legacy purge configuration is available', $context + [
'url_count' => count($urls),
]);
return false;
}
private function purgeViaCloudflare(array $urls, array $context): bool
{
$purgeUrl = sprintf(
'https://api.cloudflare.com/client/v4/zones/%s/purge_cache',
trim((string) config('cdn.cloudflare.zone_id')),
);
try {
$response = Http::timeout(10)
->acceptJson()
->withToken(trim((string) config('cdn.cloudflare.api_token')))
->post($purgeUrl, ['files' => $urls]);
if ($response->successful()) {
return true;
}
Log::warning('Cloudflare artwork CDN purge failed', $context + [
'status' => $response->status(),
'body' => $response->body(),
'url_count' => count($urls),
]);
} catch (\Throwable $e) {
Log::warning('Cloudflare artwork CDN purge threw an exception', $context + [
'error' => $e->getMessage(),
'url_count' => count($urls),
]);
}
return false;
}
private function purgeViaLegacyWebhook(string $purgeUrl, array $urls, array $context): bool
{
$paths = array_values(array_unique(array_filter(array_map(function (string $url): ?string {
$path = parse_url($url, PHP_URL_PATH);
return is_string($path) && $path !== '' ? $path : null;
}, $urls))));
if ($paths === []) {
return false;
}
try {
$response = Http::timeout(10)->acceptJson()->post($purgeUrl, ['paths' => $paths]);
if ($response->successful()) {
return true;
}
Log::warning('Legacy artwork CDN purge failed', $context + [
'status' => $response->status(),
'body' => $response->body(),
'path_count' => count($paths),
]);
} catch (\Throwable $e) {
Log::warning('Legacy artwork CDN purge threw an exception', $context + [
'error' => $e->getMessage(),
'path_count' => count($paths),
]);
}
return false;
}
private function hasCloudflareCredentials(): bool
{
return trim((string) config('cdn.cloudflare.zone_id', '')) !== ''
&& trim((string) config('cdn.cloudflare.api_token', '')) !== '';
}
private function cdnUrlForObjectPath(string $objectPath): string
{
return rtrim((string) config('cdn.files_url', 'https://cdn.skinbase.org'), '/') . '/' . ltrim($objectPath, '/');
}
}