Files
michaelschiemer/src/Application/Api/Images/ListImagesController.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

168 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Application\Api\Images;
use App\Domain\Media\Image;
use App\Framework\Attributes\Route;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\Responses\JsonResponse;
use App\Framework\Http\Status;
use App\Framework\Pagination\PaginationService;
use App\Framework\Pagination\ValueObjects\Direction;
final readonly class ListImagesController
{
public function __construct(
private PaginationService $paginationService
) {
}
#[Route(path: '/api/images', method: Method::GET)]
public function __invoke(HttpRequest $request): JsonResponse
{
// Parse query parameters
$limit = $this->parseLimit($request);
$page = $this->parsePage($request);
$sortField = $this->parseSortField($request);
$direction = $this->parseDirection($request);
$cursor = $this->parseCursor($request);
// Create pagination request
if ($cursor !== null) {
// Cursor-based pagination
$paginationRequest = $this->paginationService->cursorRequest(
limit: $limit,
cursorValue: $cursor,
sortField: $sortField,
direction: $direction
);
} else {
// Offset-based pagination
$offset = ($page - 1) * $limit;
$paginationRequest = $this->paginationService->offsetRequest(
limit: $limit,
offset: $offset,
sortField: $sortField,
direction: $direction
);
}
// Get paginated results
$paginator = $this->paginationService->forEntity(Image::class);
$paginationResponse = $paginator->paginate($paginationRequest);
// Transform images to API format
$transformedData = array_map([$this, 'transformImage'], $paginationResponse->data);
// Create response with transformed data
$responseData = [
'data' => $transformedData,
'meta' => $paginationResponse->meta->toArray(),
];
return new JsonResponse($responseData, Status::OK);
}
/**
* Parse limit parameter with validation
*/
private function parseLimit(HttpRequest $request): int
{
$limit = $request->query->getInt('limit', 20);
// Validate limit bounds
if ($limit < 1) {
$limit = 1;
} elseif ($limit > 100) {
$limit = 100;
}
return $limit;
}
/**
* Parse page parameter with validation
*/
private function parsePage(HttpRequest $request): int
{
$page = $request->query->getInt('page', 1);
return max(1, $page);
}
/**
* Parse sort field parameter
*/
private function parseSortField(HttpRequest $request): ?string
{
$sortField = $request->query->getString('sort');
// Allow only specific fields for security
$allowedFields = ['ulid', 'filename', 'width', 'height', 'fileSize'];
if ($sortField && in_array($sortField, $allowedFields)) {
return $sortField;
}
// Default sort by creation time (via ULID)
return 'ulid';
}
/**
* Parse direction parameter
*/
private function parseDirection(HttpRequest $request): string
{
$direction = $request->query->getString('direction', 'desc');
return in_array($direction, ['asc', 'desc']) ? $direction : 'desc';
}
/**
* Parse cursor parameter
*/
private function parseCursor(HttpRequest $request): ?string
{
return $request->query->getString('cursor');
}
/**
* Transform Image entity to API representation
*/
private function transformImage(Image $image): array
{
return [
'ulid' => $image->getUlidString(),
'filename' => $image->filename,
'original_filename' => $image->originalFilename,
'url' => '/images/' . $image->filename,
'thumbnail_url' => '/images/' . str_replace('_original.', '_thumbnail.', $image->filename),
'alt_text' => $image->altText,
'dimensions' => [
'width' => $image->width,
'height' => $image->height,
'aspect_ratio' => $image->getAspectRatio(),
'orientation' => $image->getDimensions()->getOrientation()->value,
],
'mime_type' => $image->mimeType->value,
'file_size' => [
'bytes' => $image->fileSize->toBytes(),
'human_readable' => $image->getHumanReadableFileSize(),
],
'hash' => $image->hash->toString(),
'is_image' => $image->isImageFile(),
'created_at' => $image->ulid->getDateTime()->format('c'),
'variants' => array_map(fn ($variant) => [
'type' => $variant->variantType,
'width' => $variant->width,
'height' => $variant->height,
'path' => $variant->path,
'url' => '/images/' . $variant->filename,
], $image->variants ?? []),
];
}
}