feat(admin): rebuild release upload form with Livewire and CDN validation
Replace the server-rendered release upload form with a Livewire component, introducing an interactive admin release form that supports dynamic episode management, real‑time CDN path validation, and direct download file checks. Key changes: - New `AdminReleaseForm` Livewire component with dynamic episode fields, Tagify integration, and upload handling via Livewire's file uploads. - Added `CdnPathValidator` service to verify download and stream paths across all configured mirrors, caching results and capturing file sizes. - Extended `EpisodeService` and `GalleryService` to support array‑based data and temporary uploaded files from Livewire. - Persist validated download sizes and store the validation timestamp on the `downloads` table via a new migration; also add a unique constraint on `hentais.slug` to prevent duplicates. - Remove the old POST `/admin/release/upload` route and controller logic, along with the legacy `upload.js` script. - Add comprehensive feature and unit tests covering the new component and the CDN validation service.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
@props(['status' => null])
|
||||
|
||||
@php
|
||||
$state = is_array($status) ? ($status['state'] ?? 'idle') : 'idle';
|
||||
$message = is_array($status) ? ($status['message'] ?? '') : '';
|
||||
$mirrors = is_array($status) ? ($status['mirrors'] ?? []) : [];
|
||||
$missing = [];
|
||||
foreach ($mirrors as $domain => $paths) {
|
||||
foreach ((array) $paths as $ok) {
|
||||
if (! $ok) {
|
||||
$missing[] = preg_replace('#^https?://#', '', $domain);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
@if ($state === 'checking')
|
||||
<div class="flex items-center gap-2 text-xs text-blue-500">
|
||||
<svg class="w-3.5 h-3.5 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span>{{ $message }}</span>
|
||||
</div>
|
||||
@elseif ($state === 'valid')
|
||||
<div class="flex items-center gap-2 text-xs text-green-600 dark:text-green-400">
|
||||
<svg class="w-3.5 h-3.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
|
||||
<span>{{ $message }}</span>
|
||||
</div>
|
||||
@elseif ($state === 'invalid')
|
||||
<div class="flex items-center gap-2 text-xs text-red-500">
|
||||
<svg class="w-3.5 h-3.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v4m0 4h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/></svg>
|
||||
<div>
|
||||
<span>{{ $message }}</span>
|
||||
@if (! empty($missing))
|
||||
<div class="mt-0.5 text-[10px] text-red-400">Missing: {{ implode(', ', $missing) }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
Reference in New Issue
Block a user