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.
92 lines
2.3 KiB
PHP
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);
|
|
}
|
|
}
|
|
}
|