- ImageUploadService::store() now returns /assets/uploads/<token>/<file>
so the URL the upload handler emits matches the new GET route.
- public/index.php registers a dedicated GET /assets/uploads/{userToken}/{filename}
route; the legacy /assets/{kind}/{filename} is kept as a fallback.
- GetAssets reads the upload token from $request->server['__uploads_token']
(the front controller threads it that way) instead of the cookies bag.
- Adds GetAssets unit tests for the upload-token happy path and a
token-mismatch 404, plus a FullFlowTest step that uploads and re-fetches
the asset through the front controller.
39 lines
1.1 KiB
PHP
39 lines
1.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace BattleForge\Application;
|
|
|
|
final class ImageUploadService
|
|
{
|
|
public function __construct(
|
|
private readonly string $userToken,
|
|
private readonly string $uploadsRoot,
|
|
) {
|
|
}
|
|
|
|
public function store(string $tempPath, string $declaredMime): string
|
|
{
|
|
$validated = ImageValidator::validate($tempPath, $declaredMime);
|
|
|
|
$namespace = $this->uploadsRoot . '/' . $this->userToken;
|
|
if (!is_dir($namespace)) {
|
|
mkdir($namespace, 0700, true);
|
|
}
|
|
|
|
$hash = bin2hex(random_bytes(16));
|
|
$filename = $hash . '.' . $validated->extension;
|
|
$destination = $namespace . '/' . $filename;
|
|
|
|
if (!rename($tempPath, $destination)) {
|
|
throw new InvalidImageException("Could not move upload to {$destination}.");
|
|
}
|
|
|
|
// Lock the file down: the namespace is already 0700; tighten the
|
|
// file itself to 0600 in case the server's umask is permissive.
|
|
chmod($destination, 0600);
|
|
|
|
return '/assets/uploads/' . $this->userToken . '/' . $filename;
|
|
}
|
|
}
|