Files
michaelschiemer/src/Application/Admin/ShowImageUpload.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

188 lines
6.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Application\Admin;
use App\Application\Admin\Service\AdminLayoutProcessor;
use App\Domain\Media\Image;
use App\Domain\Media\ImageRepository;
use App\Domain\Media\ImageResizer;
use App\Domain\Media\ImageVariantRepository;
use App\Framework\Attributes\Route;
use App\Framework\Auth\Auth;
use App\Framework\Core\PathProvider;
use App\Framework\Http\Method;
use App\Framework\Http\Request;
use App\Framework\Http\Session\FormIdGenerator;
use App\Framework\Http\UploadedFile;
use App\Framework\Meta\MetaData;
use App\Framework\Router\Result\ViewResult;
use App\Framework\Ulid\StringConverter;
use App\Framework\Ulid\Ulid;
use App\Framework\View\FormBuilder;
use App\Framework\View\RawHtml;
final readonly class ShowImageUpload
{
public function __construct(
private PathProvider $pathProvider,
private StringConverter $stringConverter,
private FormIdGenerator $formIdGenerator,
private AdminLayoutProcessor $layoutProcessor,
) {
}
#[Auth]
#[Route('/upload')]
public function __invoke(): ViewResult
{
$form = FormBuilder::create('/upload', 'post', $this->formIdGenerator)
->withClass('upload-form')
->addFileInput('image', 'Bild hochladen:', true)
->addSubmitButton('Upload');
// Set enctype for file upload
$formHtml = str_replace('<form', '<form enctype="multipart/form-data"', (string) $form);
$data = [
'title' => 'Bild-Upload',
'description' => 'Laden Sie neue Bilder in das System hoch.',
'formHtml' => RawHtml::from($formHtml),
];
$finalData = $this->layoutProcessor->processLayoutFromArray($data);
$metaData = MetaData::create(
title: 'Bild-Upload | Admin Panel',
description: 'Upload new images to the system'
);
return new ViewResult('upload-form', $metaData, $finalData);
}
#[Auth]
#[Route('/upload', Method::POST)]
public function upload(Request $request, Ulid $ulid, ImageRepository $imageRepository, ImageVariantRepository $imageVariantRepository): ViewResult
{
try {
/** @var UploadedFile $file */
$file = $request->files->get('image');
if (! $file || $file->error !== UPLOAD_ERR_OK) {
return $this->renderUploadError('Keine gültige Datei hochgeladen.');
}
$storageFolder = $this->pathProvider->resolvePath('/storage');
$uploadDirectory = sprintf('uploads/%s/%s/%s', date('Y'), date('m'), date('d'));
$ulid = (string)$ulid;
$id = substr($ulid, 10); // Remove Timestamp
$hash = hash_file('sha256', $file->tmpName);
// Prüfen, ob ein Bild mit diesem Hash bereits existiert
$existingImage = $imageRepository->findByHash($hash);
if ($existingImage !== null) {
return $this->renderUploadSuccess(
'Bild bereits vorhanden',
"Dieses Bild wurde bereits hochgeladen. Bild-ID: {$existingImage->ulid}"
);
}
$idStr = str_pad((string)$id, 9, '0', STR_PAD_LEFT);
$filePathPattern = sprintf(
'%s/%s/%s',
substr($idStr, 0, 3),
substr($idStr, 3, 3),
substr($idStr, 6, 3),
);
$path = $storageFolder . '/' . $uploadDirectory . '/' . $filePathPattern . "/";
$filename = $idStr . '_' . $hash . "_";
[$width, $height] = getimagesize($file->tmpName);
$image = new Image(
ulid : $ulid,
filename : $filename . 'original.jpg',
originalFilename: $file->name,
mimeType : $file->getMimeType(),
fileSize : $file->size,
width : $width,
height : $height,
hash : $hash,
path : $path,
altText : 'Uploaded image',
);
$imageRepository->save($image, $file->tmpName);
// Create thumbnail variant
$variant = new ImageResizer()($image, 150, 150);
$imageVariantRepository->save($variant);
return $this->renderUploadSuccess(
'Upload erfolgreich!',
"Bild wurde erfolgreich hochgeladen.<br>" .
"Original: {$image->filename}<br>" .
"Thumbnail: {$variant->filename}<br>" .
"ULID: {$image->ulid}"
);
} catch (\Exception $e) {
return $this->renderUploadError(
"Fehler beim Upload: " . $e->getMessage()
);
}
}
private function renderUploadError(string $message): ViewResult
{
$data = [
'title' => 'Upload Fehler',
'description' => $message,
'error' => true,
'formHtml' => $this->buildUploadForm(),
];
$finalData = $this->layoutProcessor->processLayoutFromArray($data);
return new ViewResult(
'upload-form',
MetaData::create('Upload Fehler | Admin Panel', $message),
$finalData
);
}
private function renderUploadSuccess(string $title, string $message): ViewResult
{
$data = [
'title' => $title,
'description' => $message,
'success' => true,
'formHtml' => $this->buildUploadForm(),
];
$finalData = $this->layoutProcessor->processLayoutFromArray($data);
return new ViewResult(
'upload-form',
MetaData::create($title . ' | Admin Panel', $message),
$finalData
);
}
private function buildUploadForm(): RawHtml
{
$form = FormBuilder::create('/upload', 'post', $this->formIdGenerator)
->withClass('upload-form')
->addFileInput('image', 'Bild hochladen:', true)
->addSubmitButton('Upload');
$formHtml = str_replace('<form', '<form enctype="multipart/form-data"', (string) $form);
return RawHtml::from($formHtml);
}
}