refs #1555. Install Mercure

pull/21/head
Manuel Aranda Rosales 2025-02-20 16:17:13 +01:00
parent f9feafa9b8
commit 560559bba9
26 changed files with 556 additions and 30 deletions

6
.env
View File

@ -50,3 +50,9 @@ UDS_AUTH_USERNAME="natiqindel"
UDS_AUTH_PASSWORD="correct horse battery staple"
UDS_URL=https://localhost:8087/uds/rest/
###< UDS ###
###> symfony/mercure-bundle ###
MERCURE_URL=http://ogcore-mercure:3000/.well-known/mercure
MERCURE_PUBLIC_URL=http://ogcore-mercure:3000/.well-known/mercure
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
###< symfony/mercure-bundle ###

View File

@ -5,3 +5,9 @@ services:
ports:
- "5432"
###< doctrine/doctrine-bundle ###
###> symfony/mercure-bundle ###
mercure:
ports:
- "80"
###< symfony/mercure-bundle ###

View File

@ -27,6 +27,7 @@
"symfony/flex": "^2",
"symfony/framework-bundle": "6.4.*",
"symfony/http-client": "6.4.*",
"symfony/mercure-bundle": "^0.3.9",
"symfony/monolog-bundle": "^3.10",
"symfony/property-access": "6.4.*",
"symfony/property-info": "6.4.*",

169
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2df97d4a1797242acadb47d83b3fbe98",
"content-hash": "ac4764e765324b86e616d7eb47a55e63",
"packages": [
{
"name": "api-platform/core",
@ -4981,6 +4981,173 @@
],
"time": "2024-11-27T12:49:36+00:00"
},
{
"name": "symfony/mercure",
"version": "v0.6.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/mercure.git",
"reference": "304cf84609ef645d63adc65fc6250292909a461b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mercure/zipball/304cf84609ef645d63adc65fc6250292909a461b",
"reference": "304cf84609ef645d63adc65fc6250292909a461b",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"symfony/deprecation-contracts": "^2.0|^3.0|^4.0",
"symfony/http-client": "^4.4|^5.0|^6.0|^7.0",
"symfony/http-foundation": "^4.4|^5.0|^6.0|^7.0",
"symfony/polyfill-php80": "^1.22",
"symfony/web-link": "^4.4|^5.0|^6.0|^7.0"
},
"require-dev": {
"lcobucci/jwt": "^3.4|^4.0|^5.0",
"symfony/event-dispatcher": "^4.4|^5.0|^6.0|^7.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0|^7.0",
"symfony/phpunit-bridge": "^5.2|^6.0|^7.0",
"symfony/stopwatch": "^4.4|^5.0|^6.0|^7.0",
"twig/twig": "^2.0|^3.0|^4.0"
},
"suggest": {
"symfony/stopwatch": "Integration with the profiler performances"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/dunglas/mercure",
"name": "dunglas/mercure"
},
"branch-alias": {
"dev-main": "0.6.x-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Mercure\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kévin Dunglas",
"email": "dunglas@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Mercure Component",
"homepage": "https://symfony.com",
"keywords": [
"mercure",
"push",
"sse",
"updates"
],
"support": {
"issues": "https://github.com/symfony/mercure/issues",
"source": "https://github.com/symfony/mercure/tree/v0.6.5"
},
"funding": [
{
"url": "https://github.com/dunglas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/mercure",
"type": "tidelift"
}
],
"time": "2024-04-08T12:51:34+00:00"
},
{
"name": "symfony/mercure-bundle",
"version": "v0.3.9",
"source": {
"type": "git",
"url": "https://github.com/symfony/mercure-bundle.git",
"reference": "77435d740b228e9f5f3f065b6db564f85f2cdb64"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mercure-bundle/zipball/77435d740b228e9f5f3f065b6db564f85f2cdb64",
"reference": "77435d740b228e9f5f3f065b6db564f85f2cdb64",
"shasum": ""
},
"require": {
"lcobucci/jwt": "^3.4|^4.0|^5.0",
"php": ">=7.1.3",
"symfony/config": "^4.4|^5.0|^6.0|^7.0",
"symfony/dependency-injection": "^4.4|^5.4|^6.0|^7.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0|^7.0",
"symfony/mercure": "^0.6.1",
"symfony/web-link": "^4.4|^5.0|^6.0|^7.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.3.7|^5.0|^6.0|^7.0",
"symfony/stopwatch": "^4.3.7|^5.0|^6.0|^7.0",
"symfony/ux-turbo": "*",
"symfony/var-dumper": "^4.3.7|^5.0|^6.0|^7.0"
},
"suggest": {
"symfony/messenger": "To use the Messenger integration"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
"dev-main": "0.3.x-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Bundle\\MercureBundle\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kévin Dunglas",
"email": "dunglas@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony MercureBundle",
"homepage": "https://symfony.com",
"keywords": [
"mercure",
"push",
"sse",
"updates"
],
"support": {
"issues": "https://github.com/symfony/mercure-bundle/issues",
"source": "https://github.com/symfony/mercure-bundle/tree/v0.3.9"
},
"funding": [
{
"url": "https://github.com/dunglas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/mercure-bundle",
"type": "tidelift"
}
],
"time": "2024-05-31T09:07:18+00:00"
},
{
"name": "symfony/monolog-bridge",
"version": "v6.4.13",

View File

@ -7,6 +7,7 @@ resources:
groups: ['default', 'client:read']
denormalizationContext:
groups: ['client:write']
operations:
ApiPlatform\Metadata\GetCollection:
provider: App\State\Provider\ClientProvider

View File

@ -17,4 +17,5 @@ return [
Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true],
DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\MercureBundle\MercureBundle::class => ['all' => true],
];

View File

@ -5,6 +5,9 @@ api_platform:
path_segment_name_generator: api_platform.path_segment_name_generator.dash
defaults:
pagination_client_items_per_page: true
mercure:
enabled: true
collection:
pagination:
items_per_page_parameter_name: 'itemsPerPage'

View File

@ -0,0 +1,8 @@
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '*'

View File

@ -20,11 +20,10 @@ when@test:
monolog:
handlers:
main:
type: fingers_crossed
action_level: error
handler: nested
excluded_http_codes: [404, 405]
channels: ["!event"]
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: error
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"

View File

@ -42,7 +42,31 @@ services:
networks:
- ogcore-network
mercure:
image: dunglas/mercure
restart: unless-stopped
container_name: ogcore-mercure
environment:
# Uncomment the following line to disable HTTPS,
SERVER_NAME: ':3000'
MERCURE_PUBLISHER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_SUBSCRIBER_JWT_KEY: '!ChangeThisMercureHubJWTSecretKey!'
MERCURE_EXTRA_DIRECTIVES: |
cors_origins *
# Comment the following line to disable the development mode
command: /usr/bin/caddy run --config /etc/caddy/dev.Caddyfile
ports:
- "3000:3000"
volumes:
- mercure_data:/data
- mercure_config:/config
networks:
- ogcore-network
volumes:
mercure_data:
mercure_config:
database_data:
networks:

View File

@ -15,6 +15,17 @@ server {
ssl_certificate /etc/nginx/certs/ogcore.uds-test.net.crt.pem;
ssl_certificate_key /etc/nginx/certs/ogcore.uds-test.net.key.pem;
location /.well-known/mercure {
proxy_pass https://mercure:3000/.well-known/mercure;
proxy_read_timeout 24h;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /opengnsys/rest/ous// {
rewrite ^/opengnsys/rest/ous//([0-9]+)/images /opengnsys/rest/ous/$1/images;
rewrite ^/opengnsys/rest/ous//([0-9]+)/labs /opengnsys/rest/ous/$1/labs;

View File

@ -15,6 +15,7 @@
<server name="SHELL_VERBOSITY" value="-1" />
<server name="SYMFONY_PHPUNIT_REMOVE" value="" />
<server name="SYMFONY_PHPUNIT_VERSION" value="9.6" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
</php>
<testsuites>

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Client;
use App\Entity\Trace;
use App\Model\ClientStatus;
use App\Model\TraceStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
#[AsCommand(name: 'opengnsys:test', description: 'Hello PhpStorm')]
class TestCommand extends Command
{
public function __construct(
private readonly HubInterface $hub,
private readonly EntityManagerInterface $entityManager
)
{
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$trace = $this->entityManager->getRepository(Trace::class)->find(7236);
$trace->setStatus(TraceStatus::SUCCESS);
$trace->setProgress(1000);
$this->entityManager->persist($trace);
$this->entityManager->flush();
return Command::SUCCESS;
}
}

View File

@ -47,8 +47,6 @@ class DeployImageAction extends AbstractController
throw new ValidatorException('IP is required');
}
$partitionInfo = json_decode($image->getPartitionInfo(), true);
$method = match ($input->method) {
DeployMethodTypes::MULTICAST_UFTP_DIRECT, DeployMethodTypes::MULTICAST_UDPCAST_DIRECT, => 'multicast-direct',
DeployMethodTypes::MULTICAST, DeployMethodTypes::MULTICAST_UFTP, DeployMethodTypes::MULTICAST_UDPCAST => 'multicast',

View File

@ -41,12 +41,17 @@ class PowerOffAction extends AbstractController
public function __invoke(MultipleClientsInput $input): JsonResponse
{
foreach ($input->clients as $clientEntity) {
/** @var Client $client */
$client = $clientEntity->getEntity();
if (!$client->getIp()) {
throw new ValidatorException('IP is required');
}
if ($client->getStatus() === ClientStatus::OFF) {
continue;
}
$data = [
'nfn' => 'Apagar',
'ids' => '0'
@ -70,7 +75,7 @@ class PowerOffAction extends AbstractController
$jobId = json_decode($response->getContent(), true)['job_id'];
$client->setStatus(ClientStatus::OFF);
$client->setStatus(ClientStatus::TURNING_OFF);
$this->entityManager->persist($client);
$this->entityManager->flush();

View File

@ -34,6 +34,11 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
#[AsController]
class ClientsController extends AbstractController
{
const string CREATE_IMAGE = 'RESPUESTA_CrearImagen';
const string RESTORE_IMAGE = 'RESPUESTA_RestaurarImagen';
const string CONFIGURE_IMAGE = 'RESPUESTA_Configurar';
public function __construct(
protected readonly EntityManagerInterface $entityManager,
public readonly CreateAuxFilesAction $createAuxFilesAction,
@ -54,7 +59,7 @@ class ClientsController extends AbstractController
public function index(Request $request): JsonResponse
{
$data = $request->toArray();
$requiredFields = ['nfn', 'res', 'der', 'job_id'];
$requiredFields = ['job_id'];
foreach ($requiredFields as $field) {
if (!isset($data[$field])) {
@ -64,7 +69,16 @@ class ClientsController extends AbstractController
$this->logger->info('Webhook data received', $data);
if ($data['nfn'] === 'RESPUESTA_CrearImagen') {
if (isset($data['progress'])){
$trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]);
if ($trace){
$trace->setProgress($data['progress'] * 1000);
$this->entityManager->persist($trace);
$this->entityManager->flush();
}
}
if (isset($data['nfn']) && $data['nfn'] === self::CREATE_IMAGE) {
$trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]);
/** @var ImageImageRepository $imageImageRepository */
$imageImageRepository = $this->entityManager->getRepository(ImageImageRepository::class)->findOneBy(['uuid' => $data['idi']]);
@ -82,10 +96,8 @@ class ClientsController extends AbstractController
if ($data['res'] === 1) {
$trace->setStatus(TraceStatus::SUCCESS);
$trace->setFinishedAt(new \DateTime());
$imageImageRepository->setStatus(ImageStatus::AUX_FILES_PENDING);
$imageImageRepository->setCreated(true);
$this->entityManager->persist($imageImageRepository);
$this->logger->info('Start partition creation. ', ['image' => (string) $imageImageRepository->getUuid()]);
@ -95,11 +107,13 @@ class ClientsController extends AbstractController
$this->logger->info('Starting software profile creation. ', ['image' => (string) $imageImageRepository->getUuid()]);
$this->createSoftwareProfile($data['inv_sft'], $imageImageRepository);
$this->logger->info('Start aux files ogrepo API ', ['image' => (string) $imageImageRepository->getUuid()]);
try {
$this->createAuxFilesAction->__invoke($imageImageRepository);
} catch (\Exception $e) {
$this->logger->error('Error creating aux files', ['image' => (string) $imageImageRepository->getUuid(), 'error' => $e->getMessage()]);
}
$this->logger->info('End aux files ogrepo API ', ['image' => (string) $imageImageRepository->getUuid()]);
} else {
$trace->setStatus(TraceStatus::FAILED);
@ -119,9 +133,8 @@ class ClientsController extends AbstractController
$this->logger->info('Image updated. Success.', ['image' => (string) $imageImageRepository->getUuid()]);
}
if ($data['nfn'] === 'RESPUESTA_RestaurarImagen'|| $data['nfn'] === 'RESPUESTA_Configurar') {
if (isset($data['nfn']) && ($data['nfn'] === self::RESTORE_IMAGE || $data['nfn'] === self::CONFIGURE_IMAGE)) {
$trace = $this->entityManager->getRepository(Trace::class)->findOneBy(['jobId' => $data['job_id']]);
$client = $trace->getClient();
if ($data['res'] === 1) {

View File

@ -45,7 +45,7 @@ class CreateAuxFilesAction extends AbstractOgRepositoryController
$this->logger->info('Creating aux files', ['image' => $image->getName()]);
$repository = $image->getClient()->getRepository();
$repository = $data->getRepository();
$content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/torrentsum', $params);

View File

@ -37,8 +37,6 @@ class DeleteTrashAction extends AbstractOgRepositoryController
$this->logger->info('Deleting image', ['image' => $image->getName()]);
$repository = $image->getClient()->getRepository();
$content = $this->createRequest('DELETE', 'http://'.$repository->getIp().':8006/ogrepository/v1/images/'.$imageImageRepository->getImageFullsum().'?method=trash');
$this->logger->info('Image deleted', ['image' => $image->getName()]);

View File

@ -44,7 +44,7 @@ class RecoverAction extends AbstractOgRepositoryController
$this->logger->info('Recovering image', ['image' => $image->getName()]);
$repository = $image->getClient()->getRepository();
$repository = $data->getRepository();
$content = $this->createRequest('POST', 'http://'.$repository->getIp().':8006/ogrepository/v1/trash/images', $params);

View File

@ -35,7 +35,7 @@ final class TraceOutput extends AbstractOutput
public ?\DateTimeInterface $finishedAt = null;
#[Groups(['trace:read'])]
public ?int $progress = null;
public ?float $progress = null;
#[Groups(['trace:read'])]
public \DateTime $createdAt;
@ -59,7 +59,7 @@ final class TraceOutput extends AbstractOutput
$this->output = $trace->getOutput();
$this->input = $trace->getInput();
$this->finishedAt = $trace->getFinishedAt();
$this->progress = $trace->getProgress();
$this->progress = $trace->getProgress() / 100;
$this->createdAt = $trace->getCreatedAt();
$this->createdBy = $trace->getCreatedBy();
}

View File

@ -2,6 +2,7 @@
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\ClientRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
@ -152,6 +153,7 @@ class Client extends AbstractEntity
return $this->status;
}
#[ORM\PostPersist]
public function setStatus(?string $status): static
{
$this->status = $status;

View File

@ -0,0 +1,39 @@
<?php
namespace App\EventListener;
use App\Entity\Client;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
use Doctrine\ORM\Events;
use Doctrine\ORM\Event\PostUpdateEventArgs;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
#[AsEntityListener(event: Events::postUpdate, method: 'postUpdate', entity: Client::class)]
class ClientStatusNotifier
{
private LoggerInterface $logger;
public function __construct(
LoggerInterface $logger,
private readonly HubInterface $hub
)
{
$this->logger = $logger;
}
public function postUpdate(Client $client, PostUpdateEventArgs $event): void
{
$update = new Update(
'clients',
json_encode(['@id' => '/clients/'.$client->getUuid(), 'status' => $client->getStatus()])
);
$this->hub->publish($update);
$this->logger->info('Evento Mercure disparado. Cambio en el estado de un cliente: ', [
'client' => $client->getUuid(),
'status' => $client->getStatus(),
]);
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\EventListener;
use App\Entity\Client;
use App\Entity\Trace;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
use Doctrine\ORM\Events;
use Doctrine\ORM\Event\PostUpdateEventArgs;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
#[AsEntityListener(event: Events::postUpdate, method: 'postUpdate', entity: Trace::class)]
class TraceStatusProgressNotifier
{
private LoggerInterface $logger;
public function __construct(
LoggerInterface $logger,
private readonly HubInterface $hub
)
{
$this->logger = $logger;
}
public function postUpdate(Trace $trace, PostUpdateEventArgs $event): void
{
$update = new Update(
'traces',
json_encode(['@id' => '/traces/'.$trace->getUuid(), 'status' => $trace->getStatus(), 'progress' => $trace->getProgress() / 100])
);
$this->hub->publish($update);
$this->logger->info('Evento Mercure disparado. Cambio en el estado de una traza/log: ', [
'client' => $trace->getUuid(),
'status' => $trace->getStatus(),
'progress' => $trace->getProgress(),
]);
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\EventSubscriber;
use ApiPlatform\Symfony\EventListener\EventPriorities;
use App\Dto\Output\ClientOutput;
use App\Entity\Client;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
class MercureSubscriber implements EventSubscriberInterface
{
private LoggerInterface $logger;
public function __construct(
LoggerInterface $logger,
private readonly HubInterface $hub
)
{
$this->logger = $logger;
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::VIEW => ['onKernelView', EventPriorities::POST_WRITE],
];
}
public function onKernelView(ViewEvent $event): void
{
$request = $event->getRequest();
$method = $request->getMethod();
$clientOutput = $event->getControllerResult();
if (!in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'])) {
return;
}
$method = $event->getRequest()->getMethod();
if (!$clientOutput instanceof ClientOutput || (Request::METHOD_POST !== $method && Request::METHOD_PUT !== $method && Request::METHOD_PATCH !== $method)) {
return;
}
/** @var Client $client */
$client = $clientOutput->getEntity();
$update = new Update(
'clients',
json_encode(['@id' => '/clients/'.$client->getUuid(), 'status' => $client->getStatus()])
);
$this->hub->publish($update);
$this->logger->info('Evento Mercure disparado', [
'method' => $method,
'path' => $request->getPathInfo()
]);
}
}

View File

@ -5,27 +5,20 @@ namespace App\Model;
final class ClientStatus
{
public const string OFF = 'off';
public const string INITIALIZING = 'initializing';
public const string TURNING_OFF = 'turning-off';
public const string OG_LIVE = 'og-live';
public const string BUSY = 'busy';
public const string LINUX = 'linux';
public const string LINUX_SESSION = 'linux-session';
public const string MACOS = 'macos';
public const string MACOS_SESSION = 'macos-session';
public const string WINDOWS = 'windows';
public const string WINDOWS_SESSION = 'windows-session';
private const array CLIENT_STATUSES = [
self::OFF => 'Apagado',
self::TURNING_OFF => 'Apagando',
self::INITIALIZING => 'Inicializando',
self::OG_LIVE => 'OG Live',
self::BUSY => 'Ocupado',

View File

@ -13,6 +13,18 @@
"src/ApiResource/.gitignore"
]
},
"dama/doctrine-test-bundle": {
"version": "8.2",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "main",
"version": "7.2",
"ref": "896306d79d4ee143af9eadf9b09fd34a8c391b70"
},
"files": [
"config/packages/dama_doctrine_test_bundle.yaml"
]
},
"doctrine/doctrine-bundle": {
"version": "2.13",
"recipe": {
@ -27,6 +39,18 @@
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-fixtures-bundle": {
"version": "3.7",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "3.0",
"ref": "1f5514cfa15b947298df4d771e694e578d4c204d"
},
"files": [
"src/DataFixtures/AppFixtures.php"
]
},
"doctrine/doctrine-migrations-bundle": {
"version": "3.3",
"recipe": {
@ -78,6 +102,20 @@
"config/packages/nelmio_cors.yaml"
]
},
"phpunit/phpunit": {
"version": "9.6",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "9.6",
"ref": "7364a21d87e658eb363c5020c072ecfdc12e2326"
},
"files": [
".env.test",
"phpunit.xml.dist",
"tests/bootstrap.php"
]
},
"ramsey/uuid-doctrine": {
"version": "2.1",
"recipe": {
@ -143,6 +181,27 @@
"src/Kernel.php"
]
},
"symfony/maker-bundle": {
"version": "1.61",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/mercure-bundle": {
"version": "0.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "0.3",
"ref": "528285147494380298f8f991ee8c47abebaf79db"
},
"files": [
"config/packages/mercure.yaml"
]
},
"symfony/monolog-bundle": {
"version": "3.10",
"recipe": {
@ -155,6 +214,21 @@
"config/packages/monolog.yaml"
]
},
"symfony/phpunit-bridge": {
"version": "7.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.3",
"ref": "a411a0480041243d97382cac7984f7dce7813c08"
},
"files": [
".env.test",
"bin/phpunit",
"phpunit.xml.dist",
"tests/bootstrap.php"
]
},
"symfony/routing": {
"version": "6.4",
"recipe": {
@ -218,5 +292,30 @@
"files": [
"config/packages/validator.yaml"
]
},
"symfony/web-profiler-bundle": {
"version": "6.4",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.1",
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
},
"files": [
"config/packages/web_profiler.yaml",
"config/routes/web_profiler.yaml"
]
},
"zenstruck/foundry": {
"version": "1.38",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.10",
"ref": "37c2f894cc098ab4c08874b80cccc8e2f8de7976"
},
"files": [
"config/packages/zenstruck_foundry.yaml"
]
}
}