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:
2026-08-08 13:18:57 +02:00
parent 2076517ebd
commit 30a6b080eb
19 changed files with 1503 additions and 310 deletions
@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('downloads', function (Blueprint $table) {
$table->timestamp('validated_at')->nullable()->after('size');
});
Schema::table('hentais', function (Blueprint $table) {
$table->unique('slug');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('hentais', function (Blueprint $table) {
$table->dropUnique(['slug']);
});
Schema::table('downloads', function (Blueprint $table) {
$table->dropColumn('validated_at');
});
}
};
+4 -1
View File
@@ -62,6 +62,7 @@ CREATE TABLE `downloads` (
`type` char(5) NOT NULL,
`url` varchar(255) NOT NULL,
`size` double DEFAULT NULL,
`validated_at` timestamp NULL DEFAULT NULL,
`count` bigint(20) NOT NULL DEFAULT 0,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
@@ -149,7 +150,8 @@ CREATE TABLE `hentais` (
`description` text NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
UNIQUE KEY `hentais_slug_unique` (`slug`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `jobs`;
@@ -543,3 +545,4 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (67,'2026_05_06_144
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (68,'2026_05_06_181115_create_mod_logs_table',31);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (73,'2026_07_18_115452_drop_subscription_key_from_users_table',32);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (74,'2026_07_19_141500_create_video_engagement_table',32);
INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES (75,'2026_08_04_193201_add_validated_at_to_downloads_and_unique_slug_to_hentais',33);