feat/ajout-de-fonctionnalites (#1)
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s
All checks were successful
Auto Tag Develop / tag (push) Successful in 6s
Reviewed-on: #1 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #1.
This commit is contained in:
24
src/ApiResource/Dashboard.php
Normal file
24
src/ApiResource/Dashboard.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use App\State\DashboardProvider;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/dashboard',
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: DashboardProvider::class,
|
||||
),
|
||||
],
|
||||
)]
|
||||
final class Dashboard
|
||||
{
|
||||
/** @var list<array{name: string, slug: string, giteaUrl: ?string, environments: list<array{id: int, name: string, status: string, version: string}>}> */
|
||||
public array $applications = [];
|
||||
}
|
||||
25
src/ApiResource/DeployResult.php
Normal file
25
src/ApiResource/DeployResult.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\State\DeployProcessor;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Post(
|
||||
uriTemplate: '/environments/{id}/deploy',
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
processor: DeployProcessor::class,
|
||||
),
|
||||
],
|
||||
)]
|
||||
final class DeployResult
|
||||
{
|
||||
public bool $success = false;
|
||||
public string $output = '';
|
||||
public string $tag = '';
|
||||
}
|
||||
29
src/ApiResource/EnvironmentHealth.php
Normal file
29
src/ApiResource/EnvironmentHealth.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use App\State\EnvironmentHealthProvider;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/environments/{id}/health',
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: EnvironmentHealthProvider::class,
|
||||
),
|
||||
],
|
||||
)]
|
||||
final class EnvironmentHealth
|
||||
{
|
||||
public string $status = 'not_found';
|
||||
public string $version = '';
|
||||
public string $startedAt = '';
|
||||
public float $cpuPercent = 0.0;
|
||||
public string $memoryUsage = '';
|
||||
public string $memoryLimit = '';
|
||||
public float $memoryPercent = 0.0;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiProperty;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\State\ManagedApplicationProvider;
|
||||
use App\State\MaintenanceToggleProcessor;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(
|
||||
uriTemplate: '/applications',
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: ManagedApplicationProvider::class,
|
||||
),
|
||||
new Get(
|
||||
uriTemplate: '/applications/{slug}',
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: ManagedApplicationProvider::class,
|
||||
),
|
||||
new Post(
|
||||
uriTemplate: '/applications/{slug}/maintenance',
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
denormalizationContext: ['groups' => ['app:write']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: ManagedApplicationProvider::class,
|
||||
processor: MaintenanceToggleProcessor::class,
|
||||
),
|
||||
],
|
||||
)]
|
||||
final class ManagedApplication
|
||||
{
|
||||
#[ApiProperty(identifier: true)]
|
||||
#[Groups(['app:read'])]
|
||||
public string $slug = '';
|
||||
|
||||
#[Groups(['app:read'])]
|
||||
public string $name = '';
|
||||
|
||||
#[Groups(['app:read', 'app:write'])]
|
||||
public bool $maintenance = false;
|
||||
}
|
||||
24
src/ApiResource/TagList.php
Normal file
24
src/ApiResource/TagList.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\ApiResource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use App\State\TagListProvider;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/applications/{slug}/tags',
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
provider: TagListProvider::class,
|
||||
),
|
||||
],
|
||||
)]
|
||||
final class TagList
|
||||
{
|
||||
/** @var list<string> */
|
||||
public array $tags = [];
|
||||
}
|
||||
@@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\DataFixtures;
|
||||
|
||||
use App\Entity\Application;
|
||||
use App\Entity\Environment;
|
||||
use App\Entity\LogFile;
|
||||
use App\Entity\User;
|
||||
use Doctrine\Bundle\FixturesBundle\Fixture;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
@@ -16,6 +19,14 @@ class AppFixtures extends Fixture
|
||||
) {}
|
||||
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
$this->loadUsers($manager);
|
||||
$this->loadApplications($manager);
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
private function loadUsers(ObjectManager $manager): void
|
||||
{
|
||||
$admin = new User();
|
||||
$admin->setUsername('admin');
|
||||
@@ -34,7 +45,77 @@ class AppFixtures extends Fixture
|
||||
$userBob->setRoles(['ROLE_USER']);
|
||||
$userBob->setPassword($this->passwordHasher->hashPassword($userBob, 'bob'));
|
||||
$manager->persist($userBob);
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
private function loadApplications(ObjectManager $manager): void
|
||||
{
|
||||
$sirh = new Application();
|
||||
$sirh->setName('SIRH');
|
||||
$sirh->setSlug('sirh');
|
||||
$sirh->setRegistryImage('gitea.malio.fr/malio-dev/sirh');
|
||||
$sirh->setDescription('Application de gestion des absences');
|
||||
$sirh->setGiteaUrl('https://gitea.malio.fr/malio-dev/sirh');
|
||||
|
||||
$sirhProd = new Environment();
|
||||
$sirhProd->setName('production');
|
||||
$sirhProd->setContainerName('sirh-app');
|
||||
$sirhProd->setDeployScriptPath('/home/m-tristan/workspace/SIRH/deploy/docker/deploy.sh');
|
||||
$sirhProd->setMaintenanceFilePath('/home/m-tristan/workspace/SIRH/deploy/docker/maintenance.on');
|
||||
$sirhProd->setAppUrl('http://sirh.malio-dev.fr');
|
||||
$sirh->addEnvironment($sirhProd);
|
||||
|
||||
$sirhProdLog = new LogFile();
|
||||
$sirhProdLog->setLabel('prod');
|
||||
$sirhProdLog->setPath('/home/m-tristan/workspace/SIRH/var/log/prod.log');
|
||||
$sirhProd->addLogFile($sirhProdLog);
|
||||
|
||||
$sirhCronLog = new LogFile();
|
||||
$sirhCronLog->setLabel('cron');
|
||||
$sirhCronLog->setPath('/home/m-tristan/workspace/SIRH/var/log/cron.log');
|
||||
$sirhProd->addLogFile($sirhCronLog);
|
||||
|
||||
$manager->persist($sirh);
|
||||
|
||||
$lesstime = new Application();
|
||||
$lesstime->setName('Lesstime');
|
||||
$lesstime->setSlug('lesstime');
|
||||
$lesstime->setRegistryImage('gitea.malio.fr/malio-dev/lesstime');
|
||||
$lesstime->setDescription('Application de gestion du temps');
|
||||
$lesstime->setGiteaUrl('https://gitea.malio.fr/malio-dev/lesstime');
|
||||
|
||||
$lesstimeProd = new Environment();
|
||||
$lesstimeProd->setName('production');
|
||||
$lesstimeProd->setContainerName('lesstime-app');
|
||||
$lesstimeProd->setDeployScriptPath('/home/m-tristan/workspace/lesstime/deploy/docker/deploy.sh');
|
||||
$lesstimeProd->setMaintenanceFilePath('/home/m-tristan/workspace/lesstime/deploy/docker/maintenance.on');
|
||||
$lesstimeProd->setAppUrl('http://lesstime.malio-dev.fr');
|
||||
$lesstime->addEnvironment($lesstimeProd);
|
||||
|
||||
$manager->persist($lesstime);
|
||||
|
||||
$inventory = new Application();
|
||||
$inventory->setName('Inventory');
|
||||
$inventory->setSlug('inventory');
|
||||
$inventory->setRegistryImage('gitea.malio.fr/malio-dev/inventory');
|
||||
$inventory->setDescription('Application de gestion des inventaires');
|
||||
$inventory->setGiteaUrl('https://gitea.malio.fr/malio-dev/inventory');
|
||||
|
||||
$inventoryProd = new Environment();
|
||||
$inventoryProd->setName('production');
|
||||
$inventoryProd->setContainerName('inventory-app');
|
||||
$inventoryProd->setDeployScriptPath('/home/m-tristan/workspace/inventory/deploy/docker/deploy.sh');
|
||||
$inventoryProd->setMaintenanceFilePath('/home/m-tristan/workspace/inventory/deploy/docker/maintenance.on');
|
||||
$inventoryProd->setAppUrl('http://inventory.malio-dev.fr');
|
||||
$inventory->addEnvironment($inventoryProd);
|
||||
|
||||
$inventoryRecette = new Environment();
|
||||
$inventoryRecette->setName('recette');
|
||||
$inventoryRecette->setContainerName('inventory-test-app');
|
||||
$inventoryRecette->setDeployScriptPath('/home/m-tristan/workspace/inventory/deploy/docker/deploy-test.sh');
|
||||
$inventoryRecette->setMaintenanceFilePath('/home/m-tristan/workspace/inventory/deploy/docker/maintenance-test.on');
|
||||
$inventoryRecette->setAppUrl('http://inventory-test.malio-dev.fr');
|
||||
$inventory->addEnvironment($inventoryRecette);
|
||||
|
||||
$manager->persist($inventory);
|
||||
}
|
||||
}
|
||||
|
||||
189
src/Entity/Application.php
Normal file
189
src/Entity/Application.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiProperty;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\ApplicationRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
new Get(
|
||||
uriVariables: ['slug'],
|
||||
normalizationContext: ['groups' => ['app:read', 'app:detail']],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
new Post(
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
new Patch(
|
||||
uriVariables: ['slug'],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
new Delete(
|
||||
uriVariables: ['slug'],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
],
|
||||
normalizationContext: ['groups' => ['app:read']],
|
||||
denormalizationContext: ['groups' => ['app:write']],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: ApplicationRepository::class)]
|
||||
class Application
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['app:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['app:read', 'app:write'])]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column(length: 255, unique: true)]
|
||||
#[ApiProperty(identifier: true)]
|
||||
#[Groups(['app:read', 'app:write'])]
|
||||
private ?string $slug = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['app:read', 'app:write'])]
|
||||
private ?string $registryImage = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
#[Groups(['app:read', 'app:write'])]
|
||||
private ?string $description = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['app:read', 'app:write'])]
|
||||
private ?string $giteaUrl = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
|
||||
#[Groups(['app:read'])]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
/** @var Collection<int, Environment> */
|
||||
#[ORM\OneToMany(targetEntity: Environment::class, mappedBy: 'application', cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
#[Groups(['app:read', 'app:detail'])]
|
||||
private Collection $environments;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
$this->environments = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSlug(): ?string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function setSlug(string $slug): static
|
||||
{
|
||||
$this->slug = $slug;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRegistryImage(): ?string
|
||||
{
|
||||
return $this->registryImage;
|
||||
}
|
||||
|
||||
public function setRegistryImage(string $registryImage): static
|
||||
{
|
||||
$this->registryImage = $registryImage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(?string $description): static
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGiteaUrl(): ?string
|
||||
{
|
||||
return $this->giteaUrl;
|
||||
}
|
||||
|
||||
public function setGiteaUrl(?string $giteaUrl): static
|
||||
{
|
||||
$this->giteaUrl = $giteaUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Environment> */
|
||||
public function getEnvironments(): Collection
|
||||
{
|
||||
return $this->environments;
|
||||
}
|
||||
|
||||
public function addEnvironment(Environment $environment): static
|
||||
{
|
||||
if (!$this->environments->contains($environment)) {
|
||||
$this->environments->add($environment);
|
||||
$environment->setApplication($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeEnvironment(Environment $environment): static
|
||||
{
|
||||
if ($this->environments->removeElement($environment)) {
|
||||
if ($environment->getApplication() === $this) {
|
||||
$environment->setApplication(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
198
src/Entity/Environment.php
Normal file
198
src/Entity/Environment.php
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Link;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\EnvironmentRepository;
|
||||
use App\State\MaintenanceToggleProcessor;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Post(
|
||||
uriTemplate: '/applications/{slug}/environments',
|
||||
uriVariables: [
|
||||
'slug' => new Link(fromClass: Application::class, fromProperty: 'environments'),
|
||||
],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
new Patch(
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
new Delete(
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
),
|
||||
new Post(
|
||||
uriTemplate: '/environments/{id}/maintenance',
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
denormalizationContext: ['groups' => ['maintenance:write']],
|
||||
processor: MaintenanceToggleProcessor::class,
|
||||
),
|
||||
],
|
||||
normalizationContext: ['groups' => ['env:read']],
|
||||
denormalizationContext: ['groups' => ['env:write']],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: EnvironmentRepository::class)]
|
||||
class Environment
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['env:read', 'app:detail'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private ?string $containerName = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private ?string $deployScriptPath = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private ?string $maintenanceFilePath = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private ?string $appUrl = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Application::class, inversedBy: 'environments')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?Application $application = null;
|
||||
|
||||
/** @var Collection<int, LogFile> */
|
||||
#[ORM\OneToMany(targetEntity: LogFile::class, mappedBy: 'environment', cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private Collection $logFiles;
|
||||
|
||||
#[Groups(['env:read', 'app:detail'])]
|
||||
private ?bool $maintenance = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->logFiles = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContainerName(): ?string
|
||||
{
|
||||
return $this->containerName;
|
||||
}
|
||||
|
||||
public function setContainerName(string $containerName): static
|
||||
{
|
||||
$this->containerName = $containerName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDeployScriptPath(): ?string
|
||||
{
|
||||
return $this->deployScriptPath;
|
||||
}
|
||||
|
||||
public function setDeployScriptPath(string $deployScriptPath): static
|
||||
{
|
||||
$this->deployScriptPath = $deployScriptPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaintenanceFilePath(): ?string
|
||||
{
|
||||
return $this->maintenanceFilePath;
|
||||
}
|
||||
|
||||
public function setMaintenanceFilePath(string $maintenanceFilePath): static
|
||||
{
|
||||
$this->maintenanceFilePath = $maintenanceFilePath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAppUrl(): ?string
|
||||
{
|
||||
return $this->appUrl;
|
||||
}
|
||||
|
||||
public function setAppUrl(?string $appUrl): static
|
||||
{
|
||||
$this->appUrl = $appUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplication(): ?Application
|
||||
{
|
||||
return $this->application;
|
||||
}
|
||||
|
||||
public function setApplication(?Application $application): static
|
||||
{
|
||||
$this->application = $application;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, LogFile> */
|
||||
public function getLogFiles(): Collection
|
||||
{
|
||||
return $this->logFiles;
|
||||
}
|
||||
|
||||
public function addLogFile(LogFile $logFile): static
|
||||
{
|
||||
if (!$this->logFiles->contains($logFile)) {
|
||||
$this->logFiles->add($logFile);
|
||||
$logFile->setEnvironment($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeLogFile(LogFile $logFile): static
|
||||
{
|
||||
if ($this->logFiles->removeElement($logFile)) {
|
||||
if ($logFile->getEnvironment() === $this) {
|
||||
$logFile->setEnvironment(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaintenance(): bool
|
||||
{
|
||||
return file_exists((string) $this->maintenanceFilePath);
|
||||
}
|
||||
}
|
||||
72
src/Entity/LogFile.php
Normal file
72
src/Entity/LogFile.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\LogFileRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ORM\Entity(repositoryClass: LogFileRepository::class)]
|
||||
class LogFile
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['env:read', 'app:detail'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private ?string $label = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['env:read', 'env:write', 'app:detail'])]
|
||||
private ?string $path = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Environment::class, inversedBy: 'logFiles')]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private ?Environment $environment = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): static
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPath(): ?string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function setPath(string $path): static
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEnvironment(): ?Environment
|
||||
{
|
||||
return $this->environment;
|
||||
}
|
||||
|
||||
public function setEnvironment(?Environment $environment): static
|
||||
{
|
||||
$this->environment = $environment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
20
src/Repository/ApplicationRepository.php
Normal file
20
src/Repository/ApplicationRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Application;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Application>
|
||||
*/
|
||||
class ApplicationRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Application::class);
|
||||
}
|
||||
}
|
||||
20
src/Repository/EnvironmentRepository.php
Normal file
20
src/Repository/EnvironmentRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Environment;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Environment>
|
||||
*/
|
||||
class EnvironmentRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Environment::class);
|
||||
}
|
||||
}
|
||||
20
src/Repository/LogFileRepository.php
Normal file
20
src/Repository/LogFileRepository.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\LogFile;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<LogFile>
|
||||
*/
|
||||
class LogFileRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, LogFile::class);
|
||||
}
|
||||
}
|
||||
41
src/Service/DeployService.php
Normal file
41
src/Service/DeployService.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\Environment;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
final class DeployService
|
||||
{
|
||||
/**
|
||||
* @return array{success: bool, output: string, exitCode: int}
|
||||
*/
|
||||
public function deploy(Environment $environment, string $tag): array
|
||||
{
|
||||
$scriptPath = $environment->getDeployScriptPath();
|
||||
|
||||
if (null === $scriptPath || !file_exists($scriptPath)) {
|
||||
return [
|
||||
'success' => false,
|
||||
'output' => sprintf('Deploy script not found: %s', $scriptPath ?? 'null'),
|
||||
'exitCode' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
$process = new Process(
|
||||
['bash', $scriptPath, $tag],
|
||||
dirname($scriptPath),
|
||||
);
|
||||
$process->setTimeout(300);
|
||||
|
||||
$process->run();
|
||||
|
||||
return [
|
||||
'success' => $process->isSuccessful(),
|
||||
'output' => $process->getOutput() . $process->getErrorOutput(),
|
||||
'exitCode' => $process->getExitCode() ?? 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
100
src/Service/DockerService.php
Normal file
100
src/Service/DockerService.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
final class DockerService
|
||||
{
|
||||
/**
|
||||
* @return array{status: string, image: string, version: string, startedAt: string}
|
||||
*/
|
||||
public function getContainerStatus(string $containerName): array
|
||||
{
|
||||
$process = new Process([
|
||||
'docker', 'inspect',
|
||||
'--format', '{{.State.Status}}||{{.Config.Image}}||{{.State.StartedAt}}',
|
||||
$containerName,
|
||||
]);
|
||||
$process->setTimeout(10);
|
||||
$process->run();
|
||||
|
||||
if (!$process->isSuccessful()) {
|
||||
return [
|
||||
'status' => 'not_found',
|
||||
'image' => '',
|
||||
'version' => '',
|
||||
'startedAt' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$parts = explode('||', trim($process->getOutput()));
|
||||
|
||||
if (\count($parts) < 3) {
|
||||
return [
|
||||
'status' => 'not_found',
|
||||
'image' => '',
|
||||
'version' => '',
|
||||
'startedAt' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$image = $parts[1];
|
||||
$version = 'latest';
|
||||
if (str_contains($image, ':')) {
|
||||
$version = substr($image, strrpos($image, ':') + 1);
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => $parts[0],
|
||||
'image' => $image,
|
||||
'version' => $version,
|
||||
'startedAt' => $parts[2],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{cpuPercent: float, memoryUsage: string, memoryLimit: string, memoryPercent: float}
|
||||
*/
|
||||
public function getContainerStats(string $containerName): array
|
||||
{
|
||||
$process = new Process([
|
||||
'docker', 'stats', '--no-stream',
|
||||
'--format', '{{.CPUPerc}}||{{.MemUsage}}||{{.MemPerc}}',
|
||||
$containerName,
|
||||
]);
|
||||
$process->setTimeout(10);
|
||||
$process->run();
|
||||
|
||||
if (!$process->isSuccessful()) {
|
||||
return [
|
||||
'cpuPercent' => 0.0,
|
||||
'memoryUsage' => '',
|
||||
'memoryLimit' => '',
|
||||
'memoryPercent' => 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
$parts = explode('||', trim($process->getOutput()));
|
||||
|
||||
if (\count($parts) < 3) {
|
||||
return [
|
||||
'cpuPercent' => 0.0,
|
||||
'memoryUsage' => '',
|
||||
'memoryLimit' => '',
|
||||
'memoryPercent' => 0.0,
|
||||
];
|
||||
}
|
||||
|
||||
$memParts = explode(' / ', $parts[1]);
|
||||
|
||||
return [
|
||||
'cpuPercent' => (float) rtrim($parts[0], '%'),
|
||||
'memoryUsage' => $memParts[0] ?? '',
|
||||
'memoryLimit' => $memParts[1] ?? '',
|
||||
'memoryPercent' => (float) rtrim($parts[2], '%'),
|
||||
];
|
||||
}
|
||||
}
|
||||
93
src/Service/GiteaRegistryService.php
Normal file
93
src/Service/GiteaRegistryService.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
final readonly class GiteaRegistryService
|
||||
{
|
||||
public function __construct(
|
||||
private HttpClientInterface $httpClient,
|
||||
#[Autowire('%env(GITEA_API_URL)%')]
|
||||
private string $giteaApiUrl,
|
||||
#[Autowire('%env(GITEA_API_TOKEN)%')]
|
||||
private string $giteaApiToken,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* List available tags for a container image.
|
||||
*
|
||||
* @param string $registryImage e.g. "gitea.malio.fr/malio-dev/sirh"
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function listTags(string $registryImage): array
|
||||
{
|
||||
$parts = explode('/', $registryImage);
|
||||
|
||||
if (\count($parts) < 3) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid registry image format: "%s". Expected "registry/owner/package".', $registryImage));
|
||||
}
|
||||
|
||||
$owner = $parts[1];
|
||||
$package = implode('/', \array_slice($parts, 2));
|
||||
|
||||
$bearerToken = $this->getBearerToken($owner, $package);
|
||||
|
||||
$url = sprintf('%s/v2/%s/%s/tags/list', $this->giteaApiUrl, $owner, $package);
|
||||
|
||||
$response = $this->httpClient->request('GET', $url, [
|
||||
'headers' => [
|
||||
'Authorization' => sprintf('Bearer %s', $bearerToken),
|
||||
],
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
$data = $response->toArray();
|
||||
|
||||
$tags = $data['tags'] ?? [];
|
||||
|
||||
usort($tags, function (string $a, string $b): int {
|
||||
$aIsVersion = str_starts_with($a, 'v');
|
||||
$bIsVersion = str_starts_with($b, 'v');
|
||||
|
||||
if ($aIsVersion && $bIsVersion) {
|
||||
return version_compare(ltrim($b, 'v'), ltrim($a, 'v'));
|
||||
}
|
||||
|
||||
if ($aIsVersion) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($bIsVersion) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return strcmp($a, $b);
|
||||
});
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
private function getBearerToken(string $owner, string $package): string
|
||||
{
|
||||
$tokenUrl = sprintf(
|
||||
'%s/v2/token?service=container_registry&scope=repository:%s/%s:pull',
|
||||
$this->giteaApiUrl,
|
||||
$owner,
|
||||
$package,
|
||||
);
|
||||
|
||||
$response = $this->httpClient->request('GET', $tokenUrl, [
|
||||
'auth_basic' => [$this->giteaApiToken, ''],
|
||||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
$data = $response->toArray();
|
||||
|
||||
return $data['token'] ?? throw new \RuntimeException('Failed to obtain bearer token from Gitea registry.');
|
||||
}
|
||||
}
|
||||
50
src/State/DashboardProvider.php
Normal file
50
src/State/DashboardProvider.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\ApiResource\Dashboard;
|
||||
use App\Repository\ApplicationRepository;
|
||||
use App\Service\DockerService;
|
||||
|
||||
final readonly class DashboardProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ApplicationRepository $applicationRepository,
|
||||
private DockerService $dockerService,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): Dashboard
|
||||
{
|
||||
$applications = $this->applicationRepository->findAll();
|
||||
|
||||
$dto = new Dashboard();
|
||||
|
||||
foreach ($applications as $app) {
|
||||
$envs = [];
|
||||
|
||||
foreach ($app->getEnvironments() as $env) {
|
||||
$containerStatus = $this->dockerService->getContainerStatus($env->getContainerName());
|
||||
|
||||
$envs[] = [
|
||||
'id' => $env->getId(),
|
||||
'name' => $env->getName(),
|
||||
'status' => $containerStatus['status'],
|
||||
'version' => $containerStatus['version'],
|
||||
];
|
||||
}
|
||||
|
||||
$dto->applications[] = [
|
||||
'name' => $app->getName(),
|
||||
'slug' => $app->getSlug(),
|
||||
'giteaUrl' => $app->getGiteaUrl(),
|
||||
'environments' => $envs,
|
||||
];
|
||||
}
|
||||
|
||||
return $dto;
|
||||
}
|
||||
}
|
||||
47
src/State/DeployProcessor.php
Normal file
47
src/State/DeployProcessor.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\ApiResource\DeployResult;
|
||||
use App\Repository\EnvironmentRepository;
|
||||
use App\Service\DeployService;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final readonly class DeployProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EnvironmentRepository $environmentRepository,
|
||||
private DeployService $deployService,
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): DeployResult
|
||||
{
|
||||
$id = $uriVariables['id'] ?? null;
|
||||
$environment = $id ? $this->environmentRepository->find($id) : null;
|
||||
|
||||
if (null === $environment) {
|
||||
throw new NotFoundHttpException(sprintf('Environment "%s" not found.', $id));
|
||||
}
|
||||
|
||||
$requestData = $context['request']?->toArray() ?? [];
|
||||
$tag = $requestData['tag'] ?? null;
|
||||
|
||||
if (null === $tag || '' === $tag) {
|
||||
throw new BadRequestHttpException('The "tag" field is required.');
|
||||
}
|
||||
|
||||
$result = $this->deployService->deploy($environment, $tag);
|
||||
|
||||
$dto = new DeployResult();
|
||||
$dto->success = $result['success'];
|
||||
$dto->output = $result['output'];
|
||||
$dto->tag = $tag;
|
||||
|
||||
return $dto;
|
||||
}
|
||||
}
|
||||
45
src/State/EnvironmentHealthProvider.php
Normal file
45
src/State/EnvironmentHealthProvider.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\ApiResource\EnvironmentHealth;
|
||||
use App\Repository\EnvironmentRepository;
|
||||
use App\Service\DockerService;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final readonly class EnvironmentHealthProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EnvironmentRepository $environmentRepository,
|
||||
private DockerService $dockerService,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): EnvironmentHealth
|
||||
{
|
||||
$id = $uriVariables['id'] ?? null;
|
||||
$environment = $id ? $this->environmentRepository->find($id) : null;
|
||||
|
||||
if (null === $environment) {
|
||||
throw new NotFoundHttpException(sprintf('Environment "%s" not found.', $id));
|
||||
}
|
||||
|
||||
$containerName = $environment->getContainerName();
|
||||
$status = $this->dockerService->getContainerStatus($containerName);
|
||||
$stats = $this->dockerService->getContainerStats($containerName);
|
||||
|
||||
$dto = new EnvironmentHealth();
|
||||
$dto->status = $status['status'];
|
||||
$dto->version = $status['version'];
|
||||
$dto->startedAt = $status['startedAt'];
|
||||
$dto->cpuPercent = $stats['cpuPercent'];
|
||||
$dto->memoryUsage = $stats['memoryUsage'];
|
||||
$dto->memoryLimit = $stats['memoryLimit'];
|
||||
$dto->memoryPercent = $stats['memoryPercent'];
|
||||
|
||||
return $dto;
|
||||
}
|
||||
}
|
||||
@@ -6,42 +6,26 @@ namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\ApiResource\ManagedApplication;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use App\Entity\Environment;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
final readonly class MaintenanceToggleProcessor implements ProcessorInterface
|
||||
{
|
||||
/**
|
||||
* @param list<array{name: string, slug: string, maintenance_path: string}> $managedApplications
|
||||
* @param Environment $data
|
||||
*/
|
||||
public function __construct(
|
||||
#[Autowire('%app.managed_applications%')]
|
||||
private array $managedApplications,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param ManagedApplication $data
|
||||
*/
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): ManagedApplication
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Environment
|
||||
{
|
||||
$slug = $uriVariables['slug'] ?? '';
|
||||
$appConfig = null;
|
||||
$maintenancePath = $data->getMaintenanceFilePath();
|
||||
|
||||
foreach ($this->managedApplications as $app) {
|
||||
if ($app['slug'] === $slug) {
|
||||
$appConfig = $app;
|
||||
break;
|
||||
}
|
||||
if (null === $maintenancePath) {
|
||||
throw new BadRequestHttpException('Maintenance file path is not configured for this environment.');
|
||||
}
|
||||
|
||||
if (null === $appConfig) {
|
||||
throw new NotFoundHttpException(sprintf('Application "%s" not found.', $slug));
|
||||
}
|
||||
$requestData = $context['request']?->toArray() ?? [];
|
||||
$enableMaintenance = $requestData['maintenance'] ?? false;
|
||||
|
||||
$maintenancePath = $appConfig['maintenance_path'];
|
||||
|
||||
if ($data->maintenance) {
|
||||
if ($enableMaintenance) {
|
||||
$directory = dirname($maintenancePath);
|
||||
|
||||
if (!is_dir($directory) && !mkdir($directory, 0755, true)) {
|
||||
@@ -57,11 +41,6 @@ final readonly class MaintenanceToggleProcessor implements ProcessorInterface
|
||||
}
|
||||
}
|
||||
|
||||
$dto = new ManagedApplication();
|
||||
$dto->slug = $appConfig['slug'];
|
||||
$dto->name = $appConfig['name'];
|
||||
$dto->maintenance = file_exists($maintenancePath);
|
||||
|
||||
return $dto;
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\ApiResource\ManagedApplication;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final readonly class ManagedApplicationProvider implements ProviderInterface
|
||||
{
|
||||
/**
|
||||
* @param list<array{name: string, slug: string, maintenance_path: string}> $managedApplications
|
||||
*/
|
||||
public function __construct(
|
||||
#[Autowire('%app.managed_applications%')]
|
||||
private array $managedApplications,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): ManagedApplication|array
|
||||
{
|
||||
if ($operation instanceof GetCollection) {
|
||||
return array_map(
|
||||
fn (array $app) => $this->buildDto($app),
|
||||
$this->managedApplications,
|
||||
);
|
||||
}
|
||||
|
||||
$slug = $uriVariables['slug'] ?? '';
|
||||
|
||||
foreach ($this->managedApplications as $app) {
|
||||
if ($app['slug'] === $slug) {
|
||||
return $this->buildDto($app);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException(sprintf('Application "%s" not found.', $slug));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{name: string, slug: string, maintenance_path: string} $app
|
||||
*/
|
||||
private function buildDto(array $app): ManagedApplication
|
||||
{
|
||||
$dto = new ManagedApplication();
|
||||
$dto->slug = $app['slug'];
|
||||
$dto->name = $app['name'];
|
||||
$dto->maintenance = file_exists($app['maintenance_path']);
|
||||
|
||||
return $dto;
|
||||
}
|
||||
}
|
||||
35
src/State/TagListProvider.php
Normal file
35
src/State/TagListProvider.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\State;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\ApiResource\TagList;
|
||||
use App\Repository\ApplicationRepository;
|
||||
use App\Service\GiteaRegistryService;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
final readonly class TagListProvider implements ProviderInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ApplicationRepository $applicationRepository,
|
||||
private GiteaRegistryService $giteaRegistryService,
|
||||
) {}
|
||||
|
||||
public function provide(Operation $operation, array $uriVariables = [], array $context = []): TagList
|
||||
{
|
||||
$slug = $uriVariables['slug'] ?? '';
|
||||
$application = $this->applicationRepository->findOneBy(['slug' => $slug]);
|
||||
|
||||
if (null === $application) {
|
||||
throw new NotFoundHttpException(sprintf('Application "%s" not found.', $slug));
|
||||
}
|
||||
|
||||
$dto = new TagList();
|
||||
$dto->tags = $this->giteaRegistryService->listTags($application->getRegistryImage());
|
||||
|
||||
return $dto;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user