Files
w33b 30a6b080eb 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.
2026-08-08 13:18:57 +02:00

67 lines
1.8 KiB
JavaScript

import Tagify from '@yaireo/tagify';
import '@yaireo/tagify/dist/tagify.css';
document.addEventListener('livewire:init', () => {
const Alpine = window.Alpine;
const dropdownOptions = {
classname: "color-blue",
enabled: 0,
maxItems: 10,
position: "text",
closeOnSelect: false,
highlightFirst: true
};
Alpine.data('adminReleaseTags', () => ({
tagify: null,
init() {
const el = this.$refs.tags;
window.axios.get('/admin/tags').then((response) => {
if (response.status != 200) {
return;
}
this.tagify = new Tagify(el, {
whitelist: response.data.tags,
dropdown: dropdownOptions
});
this.tagify.on('change', () => {
this.$wire.set('tags', this.tagify.value.map((t) => t.value));
});
}).catch((error) => {
console.log(error);
});
}
}));
Alpine.data('adminReleaseStudio', () => ({
tagify: null,
init() {
const el = this.$refs.studio;
window.axios.get('/admin/studios').then((response) => {
if (response.status != 200) {
return;
}
this.tagify = new Tagify(el, {
whitelist: response.data.studios,
dropdown: dropdownOptions
});
this.tagify.on('change', () => {
const values = this.tagify.value.map((t) => t.value);
this.$wire.set('studio', values.length ? values[values.length - 1] : '');
});
}).catch((error) => {
console.log(error);
});
}
}));
});