Allow heading tags (h1-h6) in ContentSanitizer so news editor headings render
This commit is contained in:
208
app/Http/Controllers/EnhanceController.php
Normal file
208
app/Http/Controllers/EnhanceController.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Models\EnhanceJob;
|
||||
use App\Services\Enhance\EnhanceService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
final class EnhanceController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EnhanceService $enhanceService,
|
||||
) {
|
||||
}
|
||||
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$this->authorize('viewAny', EnhanceJob::class);
|
||||
|
||||
$jobs = EnhanceJob::query()
|
||||
->where('user_id', (int) $request->user()->id)
|
||||
->with('artwork:id,title,slug')
|
||||
->latest('id')
|
||||
->paginate(12)
|
||||
->withQueryString()
|
||||
->through(fn (EnhanceJob $job): array => $this->serializeJobListItem($job));
|
||||
|
||||
$latestCompleted = EnhanceJob::query()
|
||||
->where('user_id', (int) $request->user()->id)
|
||||
->where('status', EnhanceJob::STATUS_COMPLETED)
|
||||
->latest('finished_at')
|
||||
->limit(4)
|
||||
->get()
|
||||
->map(fn (EnhanceJob $job): array => $this->serializeJobListItem($job))
|
||||
->all();
|
||||
|
||||
return Inertia::render('Enhance/Index', [
|
||||
'title' => 'Skinbase Enhance',
|
||||
'jobs' => $jobs,
|
||||
'latestCompleted' => $latestCompleted,
|
||||
'createUrl' => route('enhance.create'),
|
||||
'indexUrl' => route('enhance.index'),
|
||||
'dailyLimit' => (int) config('enhance.daily_limit', 10),
|
||||
'enhanceConfig' => $this->enhanceService->frontendConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
$this->authorize('create', EnhanceJob::class);
|
||||
|
||||
$selectedArtwork = null;
|
||||
|
||||
if (($artworkId = (int) $request->integer('artwork')) > 0) {
|
||||
$artwork = Artwork::query()
|
||||
->select(['id', 'user_id', 'title', 'slug'])
|
||||
->findOrFail($artworkId);
|
||||
|
||||
$actor = $request->user();
|
||||
abort_unless($actor !== null, 403);
|
||||
|
||||
$isOwner = (int) $artwork->user_id === (int) $actor->id;
|
||||
$isStaff = $actor->isAdmin() || $actor->isModerator();
|
||||
|
||||
abort_unless($isOwner || $isStaff, 403);
|
||||
|
||||
$selectedArtwork = [
|
||||
'id' => $artwork->id,
|
||||
'title' => $artwork->title,
|
||||
'show_url' => route('art.show', ['id' => $artwork->id, 'slug' => $artwork->slug]),
|
||||
'store_url' => route('artworks.enhance.store', ['artwork' => $artwork->id]),
|
||||
];
|
||||
}
|
||||
|
||||
return Inertia::render('Enhance/Create', [
|
||||
'title' => 'Skinbase Enhance',
|
||||
'options' => $this->optionsPayload(),
|
||||
'storeUrl' => route('enhance.store'),
|
||||
'indexUrl' => route('enhance.index'),
|
||||
'maxUploadMb' => (int) config('enhance.max_upload_mb', 20),
|
||||
'selectedArtwork' => $selectedArtwork,
|
||||
'enhanceConfig' => $this->enhanceService->frontendConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$this->authorize('create', EnhanceJob::class);
|
||||
|
||||
$validated = $request->validate([
|
||||
'image' => ['required', 'file', 'mimetypes:image/jpeg,image/png,image/webp', 'max:' . ((int) config('enhance.max_upload_mb', 20) * 1024)],
|
||||
'scale' => ['required', 'integer', Rule::in((array) config('enhance.allowed_scales', [2, 4]))],
|
||||
'mode' => ['required', 'string', Rule::in((array) config('enhance.allowed_modes', ['standard', 'artwork', 'photo', 'illustration']))],
|
||||
]);
|
||||
|
||||
$job = $this->enhanceService->createFromUpload($request->user(), $request->file('image'), $validated);
|
||||
|
||||
return redirect()
|
||||
->route('enhance.show', ['enhanceJob' => $job])
|
||||
->with('success', 'Enhance job created.');
|
||||
}
|
||||
|
||||
public function show(EnhanceJob $enhanceJob): Response
|
||||
{
|
||||
$this->authorize('view', $enhanceJob);
|
||||
$enhanceJob->loadMissing('artwork:id,title,slug');
|
||||
|
||||
return Inertia::render('Enhance/Show', [
|
||||
'title' => 'Enhance Job',
|
||||
'job' => $this->serializeJobDetail($enhanceJob),
|
||||
'indexUrl' => route('enhance.index'),
|
||||
'createUrl' => route('enhance.create'),
|
||||
'enhanceConfig' => $this->enhanceService->frontendConfig(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function retry(EnhanceJob $enhanceJob): RedirectResponse
|
||||
{
|
||||
$this->authorize('retry', $enhanceJob);
|
||||
|
||||
$job = $this->enhanceService->retry($enhanceJob);
|
||||
|
||||
return redirect()
|
||||
->route('enhance.show', ['enhanceJob' => $job])
|
||||
->with('success', 'Enhance job queued again.');
|
||||
}
|
||||
|
||||
public function destroy(EnhanceJob $enhanceJob): RedirectResponse
|
||||
{
|
||||
$this->authorize('delete', $enhanceJob);
|
||||
|
||||
$this->enhanceService->delete($enhanceJob);
|
||||
|
||||
return redirect()
|
||||
->route('enhance.index')
|
||||
->with('success', 'Enhance job deleted.');
|
||||
}
|
||||
|
||||
private function optionsPayload(): array
|
||||
{
|
||||
return [
|
||||
'modes' => array_map(fn (string $mode): array => [
|
||||
'value' => $mode,
|
||||
'label' => ucfirst($mode),
|
||||
], (array) config('enhance.allowed_modes', [])),
|
||||
'scales' => array_map(fn (int $scale): array => [
|
||||
'value' => $scale,
|
||||
'label' => $scale . 'x',
|
||||
], array_map('intval', (array) config('enhance.allowed_scales', []))),
|
||||
];
|
||||
}
|
||||
|
||||
private function serializeJobListItem(EnhanceJob $job): array
|
||||
{
|
||||
return [
|
||||
'id' => $job->id,
|
||||
'status' => (string) $job->status,
|
||||
'engine' => (string) $job->engine,
|
||||
'mode' => (string) $job->mode,
|
||||
'scale' => (int) $job->scale,
|
||||
'source_url' => $job->sourceUrl(),
|
||||
'output_url' => $job->outputUrl(),
|
||||
'preview_url' => $job->previewUrl(),
|
||||
'input_width' => (int) ($job->input_width ?? 0),
|
||||
'input_height' => (int) ($job->input_height ?? 0),
|
||||
'output_width' => (int) ($job->output_width ?? 0),
|
||||
'output_height' => (int) ($job->output_height ?? 0),
|
||||
'error_message' => $job->error_message,
|
||||
'processing_seconds' => $job->processing_seconds,
|
||||
'created_at' => optional($job->created_at)?->toIso8601String(),
|
||||
'finished_at' => optional($job->finished_at)?->toIso8601String(),
|
||||
'show_url' => route('enhance.show', ['enhanceJob' => $job]),
|
||||
'artwork' => $job->artwork ? [
|
||||
'id' => $job->artwork->id,
|
||||
'title' => $job->artwork->title,
|
||||
'slug' => $job->artwork->slug,
|
||||
'url' => route('art.show', ['id' => $job->artwork->id, 'slug' => $job->artwork->slug]),
|
||||
] : null,
|
||||
];
|
||||
}
|
||||
|
||||
private function serializeJobDetail(EnhanceJob $job): array
|
||||
{
|
||||
return $this->serializeJobListItem($job) + [
|
||||
'input_filesize' => (int) ($job->input_filesize ?? 0),
|
||||
'input_mime' => $job->input_mime,
|
||||
'output_filesize' => (int) ($job->output_filesize ?? 0),
|
||||
'output_mime' => $job->output_mime,
|
||||
'metadata' => $job->metadata ?? [],
|
||||
'queued_at' => optional($job->queued_at)?->toIso8601String(),
|
||||
'started_at' => optional($job->started_at)?->toIso8601String(),
|
||||
'deleted_at' => optional($job->deleted_at)?->toIso8601String(),
|
||||
'expires_at' => optional($job->expires_at)?->toIso8601String(),
|
||||
'retry_url' => route('enhance.retry', ['enhanceJob' => $job]),
|
||||
'delete_url' => route('enhance.destroy', ['enhanceJob' => $job]),
|
||||
'download_url' => $job->outputUrl(),
|
||||
'can_retry' => auth()->user()?->can('retry', $job) ?? false,
|
||||
'can_delete' => auth()->user()?->can('delete', $job) ?? false,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user