Files
Classeo/backend/src/Scolarite/Infrastructure/Storage/S3FileStorage.php
Mathias STRASSER e72867932d
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Naming Conventions (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
2026-04-13 15:44:38 +02:00

92 lines
2.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Scolarite\Infrastructure\Storage;
use App\Scolarite\Application\Port\FileStorage;
use Aws\S3\S3Client;
use function is_resource;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
use League\Flysystem\Filesystem;
use League\Flysystem\UnableToDeleteFile;
use League\Flysystem\UnableToReadFile;
use Override;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use RuntimeException;
use function sprintf;
final readonly class S3FileStorage implements FileStorage
{
private Filesystem $filesystem;
private LoggerInterface $logger;
public function __construct(
string $endpoint,
string $bucket,
string $key,
string $secret,
string $region,
?LoggerInterface $logger = null,
) {
$this->logger = $logger ?? new NullLogger();
$client = new S3Client([
'endpoint' => $endpoint,
'credentials' => [
'key' => $key,
'secret' => $secret,
],
'region' => $region,
'version' => 'latest',
'use_path_style_endpoint' => true,
]);
$this->filesystem = new Filesystem(
new AwsS3V3Adapter($client, $bucket),
);
}
#[Override]
public function upload(string $path, mixed $content, string $mimeType): string
{
$config = [
'ContentType' => $mimeType,
];
if (is_resource($content)) {
$this->filesystem->writeStream($path, $content, $config);
} else {
$this->filesystem->write($path, (string) $content, $config);
}
return $path;
}
#[Override]
public function delete(string $path): void
{
try {
$this->filesystem->delete($path);
} catch (UnableToDeleteFile $e) {
$this->logger->warning('S3 delete failed, possible orphan blob: {path}', [
'path' => $path,
'error' => $e->getMessage(),
]);
}
}
#[Override]
public function readStream(string $path): mixed
{
try {
return $this->filesystem->readStream($path);
} catch (UnableToReadFile $e) {
throw new RuntimeException(sprintf('Impossible de lire le fichier : %s', $path), 0, $e);
}
}
}