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.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user