✨ feat(artemis/infra): Ajoute la gestion des serveurs Google Compute Engine en français
This commit is contained in:
12
account.json
Normal file
12
account.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "service_account",
|
||||
"project_id": "esy-web-279616",
|
||||
"private_key_id": "1028eb629a868bdd6b0722e208a25f0b54788285",
|
||||
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCl7Iq0fY+P/Uf4\nef8Px/bU1l2/BV8dM8i7tAC1nDPztTQriw6zUQrnloAKHT6RZh5YXh0bKLVB3ZF0\nWtzx53bGw7Ufn3Xy7at1Cbk2tmamw5dYdUzRpiPFQKPr2NbIMHUEUBZkU9TlV4J4\nhLOwEvAFURRN08m2wOmRPpOvyzy0FL/ibjMY7rKuPvXfMrFtdFKsXbDT1iCV4UG8\nFwou0jKLXro/yRJ45P2KWprvS8X0dAwfHVcfLZbdT+ifW5FtCZh7xITe7UyhW+Zj\nkSZXhMbUga9AWHKzfUyesW+QvBmsJRarcwrSdn1PggJj7CICTFJDo0u9S5+mk9/+\ncsvEQAqNAgMBAAECggEABsCQiHXPoNFy21bDnXXKwrCSkhJ642pXEeRqXvmjV5iZ\nLehzybJTmkcSoNa91BbuxzPVRWSrs6l7oNuNwD5mJAKU3VuU5jTr1FB3/BUCKmkt\nQIlNff6f2AggY5moa+8k4+8KajzLHiYohhUIZvLuV5kMFXq367sABkwgoHfJ2Z4p\nxqBM3B5/x/1JxheBYhc1pgLxza40eE/mKJMP6eC1VPkru0ntxLcFbcoGKwL/dP8l\nnCGiHRFtt5Weh2wW2A9F0ZkfmdhtUyh9kIKiEZmq/AXbPm5g0+v9Mpn9omWw/Ogu\nTrsKZ5F5YWK9XSFf2HaNWKI0u1b3j05KUSX510WhQQKBgQDOZGqULtmQqNi12kQX\nMvkw3D3ExMH4fziJ8oi6t4Bpp93iAnE5q/9zdWmTxICSVGoY6zhMAsfTSmzeA6Fp\nW7aRjNDeyw3skHV/mf0Kg015AwyaAlhKOLMSh2fX+p3Ptr5dUHe90vqYzI0PVAFc\noBAIhzBzaTIViccyf+CKCM72EQKBgQDNzg+SlnFKeyuHNLUPX2Ek9ZldXFkoU6wI\nRptp9ux/S+PWLgSGF8mX04e7t5eccc+H9Rhfm+ndM4nbkfkZXgHtzASGBUeaFSQl\n/tU6nmRcudwNcIbN7yUdt8Ll+KqusEYRSfVWJ4XIyNMW+WKQ/Wjw1Vo8rfkzgVA3\nrM81zk5gvQKBgF2Oz3FUu3MD1xi4VF1f0e4AKE/mETegE/UUaD6bqC481iv5h3Hp\nMecYtj8xuEac1WtuVRq/t+1js24An06vMFdSSex0h19RLLInD7mQQ7IzW6cEoRkk\nEqi3kK8rABaEdE7Ah0cZOFfDgb4NCoD+XcY/4gqvCPESf6W4qgRoccjBAoGAO/xZ\nsaJD9y+baldEhuyIBhvHzdyC6CwrMmZSGjqsiBX4nI7hJqx8R9KR93b1q9XIZZpc\ntlFdgunovqT3dBtgeI1ErEORsSmEVcbHI8TS/+v0Zb5srE2OBfFvz1QBe9VJNvTV\nm3z9k55lWIbr4dLa6YdmO9WBky+X0AKAivBNDAkCgYBJvrnMytfe1FLQcV1EJxkY\nIfeDI7POJBnpeN/DAE5/8231rRmhqsKmTGzieKysRi+s0Yx9V4q4ixR6O9+smpXI\nAWyV3QME6AhLicuzoWobYj5wgV9vHYNMawkSgljBWLmU20aWhRpYqESr355QrPCU\nx0HwZXICbVUERaSfD1ALyQ==\n-----END PRIVATE KEY-----\n",
|
||||
"client_email": "root-user@esy-web-279616.iam.gserviceaccount.com",
|
||||
"client_id": "103002399867175151805",
|
||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||
"token_uri": "https://oauth2.googleapis.com/token",
|
||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/root-user%40esy-web-279616.iam.gserviceaccount.com"
|
||||
}
|
||||
@@ -5,6 +5,37 @@ import {AutoSubmit} from './class/AutoSubmit'
|
||||
|
||||
function script() {
|
||||
customElements.define('auto-submit',AutoSubmit,{extends:'form'})
|
||||
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', script);
|
||||
function full() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const sidebarToggle = document.getElementById('sidebar-toggle');
|
||||
|
||||
sidebarToggle.addEventListener('click', function () {
|
||||
sidebar.classList.toggle('-translate-x-full');
|
||||
});
|
||||
const submenuToggles = document.querySelectorAll('[data-submenu-toggle]');
|
||||
|
||||
submenuToggles.forEach(button => {
|
||||
button.addEventListener('click', function () {
|
||||
const targetId = this.dataset.submenuToggle;
|
||||
const submenu = document.getElementById(`submenu-${targetId}`);
|
||||
const arrowIcon = this.querySelector('.arrow-icon');
|
||||
|
||||
if (submenu && arrowIcon) {
|
||||
// Toggle the 'active' class on the submenu
|
||||
submenu.classList.toggle('active');
|
||||
// Toggle the 'rotate' class on the arrow icon
|
||||
arrowIcon.classList.toggle('rotate');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', ()=>{
|
||||
script();
|
||||
});
|
||||
document.addEventListener("turbo:load", ()=> {
|
||||
full()
|
||||
});
|
||||
|
||||
@@ -5,3 +5,16 @@ h1,h2,h3,h4,h5,h6,
|
||||
label,span,input,{
|
||||
font-family: 'Intel One Mono', monospace;
|
||||
}
|
||||
|
||||
.card-server {
|
||||
padding: 0 !important;
|
||||
.header{
|
||||
border-bottom: 1px solid var(--color-gray-700);
|
||||
padding: 0.5rem;
|
||||
background: var(--color-gray-700);
|
||||
}
|
||||
}
|
||||
|
||||
.bg-RUNNING{
|
||||
background: green;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
return svg;
|
||||
}
|
||||
|
||||
document.querySelectorAll('input[type="password"]').forEach(input=>{
|
||||
// Crée un conteneur div pour l'input et l'icône
|
||||
const wrapperDiv = document.createElement('div');
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"doctrine/doctrine-migrations-bundle": "^3.4.2",
|
||||
"doctrine/orm": "^3.5",
|
||||
"docusealco/docuseal-php": "^1.0",
|
||||
"google/cloud": "^0.296.0",
|
||||
"imagine/imagine": "^1.5",
|
||||
"knplabs/knp-paginator-bundle": "^6.8",
|
||||
"lasserafn/php-initial-avatar-generator": "^4.4",
|
||||
|
||||
1290
composer.lock
generated
1290
composer.lock
generated
File diff suppressed because it is too large
Load Diff
32
migrations/Version20250721112006.php
Normal file
32
migrations/Version20250721112006.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250721112006 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE compute (id SERIAL NOT NULL, instance_id VARCHAR(255) NOT NULL, zone VARCHAR(255) NOT NULL, internal_ip VARCHAR(255) NOT NULL, external_ip VARCHAR(255) NOT NULL, status VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('DROP TABLE compute');
|
||||
}
|
||||
}
|
||||
28
src/Controller/Artemis/Infra/ServerController.php
Normal file
28
src/Controller/Artemis/Infra/ServerController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Artemis\Infra;
|
||||
|
||||
use App\Service\Google\ComputeEngineClient;
|
||||
use LasseRafn\InitialAvatarGenerator\InitialAvatar;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class ServerController extends AbstractController
|
||||
{
|
||||
#[Route(path: '/artemis/infra/server',name: 'artemis_infrastructure_server')]
|
||||
public function artemis(Request $request,ComputeEngineClient $computeEngineClient): Response
|
||||
{
|
||||
if($request->query->has('sync')) {
|
||||
$computeEngineClient->list();
|
||||
}
|
||||
$lists = [];
|
||||
foreach ($computeEngineClient->list() as $instance) {
|
||||
$lists[] = $computeEngineClient->detail($instance);
|
||||
}
|
||||
return $this->render('artemis/infra/server.twig',[
|
||||
'lists' => $lists
|
||||
]);
|
||||
}
|
||||
}
|
||||
96
src/Entity/Compute.php
Normal file
96
src/Entity/Compute.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use AllowDynamicProperties;
|
||||
use App\Repository\ComputeRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[AllowDynamicProperties] #[ORM\Entity(repositoryClass: ComputeRepository::class)]
|
||||
class Compute
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $instanceId = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $zone = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $internalIp = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $externalIp = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $status = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getInstanceId(): ?string
|
||||
{
|
||||
return $this->instanceId;
|
||||
}
|
||||
|
||||
public function setInstanceId(string $instanceId): static
|
||||
{
|
||||
$this->instanceId = $instanceId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getZone(): ?string
|
||||
{
|
||||
return $this->zone;
|
||||
}
|
||||
|
||||
public function setZone(string $zone): static
|
||||
{
|
||||
$this->zone = $zone;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInternalIp(): ?string
|
||||
{
|
||||
return $this->internalIp;
|
||||
}
|
||||
|
||||
public function setInternalIp(string $internalIp): static
|
||||
{
|
||||
$this->internalIp = $internalIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExternalIp(): ?string
|
||||
{
|
||||
return $this->externalIp;
|
||||
}
|
||||
|
||||
public function setExternalIp(string $externalIp): static
|
||||
{
|
||||
$this->externalIp = $externalIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus(): ?string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setStatus(?string $status): static
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
43
src/Repository/ComputeRepository.php
Normal file
43
src/Repository/ComputeRepository.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Compute;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Compute>
|
||||
*/
|
||||
class ComputeRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Compute::class);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Compute[] Returns an array of Compute objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('c')
|
||||
// ->andWhere('c.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('c.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?Compute
|
||||
// {
|
||||
// return $this->createQueryBuilder('c')
|
||||
// ->andWhere('c.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
74
src/Service/Google/ComputeEngineClient.php
Normal file
74
src/Service/Google/ComputeEngineClient.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Google;
|
||||
|
||||
use App\Entity\Compute;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Google\Cloud\Compute\V1\AccessConfig;
|
||||
use Google\Cloud\Compute\V1\Client\InstancesClient;
|
||||
use Google\Cloud\Compute\V1\GetInstanceRequest;
|
||||
use Google\Cloud\Compute\V1\Instance;
|
||||
use Google\Cloud\Compute\V1\ListInstancesRequest;
|
||||
use Google\Cloud\Compute\V1\NetworkInterface;
|
||||
use Google\Protobuf\RepeatedField;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class ComputeEngineClient
|
||||
{
|
||||
private InstancesClient $client;
|
||||
private string $projectId;
|
||||
private string $zone;
|
||||
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager,KernelInterface $kernel)
|
||||
{
|
||||
$this->client = new InstancesClient([
|
||||
'credentials' => $kernel->getProjectDir()."/account.json"
|
||||
]);
|
||||
$content = file_get_contents($kernel->getProjectDir()."/account.json");
|
||||
$content = json_decode($content);
|
||||
|
||||
$this->projectId = $content->project_id;
|
||||
$this->zone = "europe-west4-a";
|
||||
}
|
||||
public function list()
|
||||
{
|
||||
$request = (new ListInstancesRequest())
|
||||
->setProject($this->projectId)
|
||||
->setZone($this->zone);
|
||||
$instancesList = $this->client->list($request);
|
||||
$instances = [];
|
||||
/** @var Instance $instance */
|
||||
foreach ($instancesList as $instance) {
|
||||
if(str_contains($instance->getName(),'srv-')) {
|
||||
/** @var NetworkInterface $network */
|
||||
$network = $instance->getNetworkInterfaces()[0];
|
||||
/** @var AccessConfig $accessConfig */
|
||||
$accessConfig = $network->getAccessConfigs()[0];
|
||||
|
||||
$compute = new Compute();
|
||||
$compute->setInstanceId($instance->getId());
|
||||
$compute->setZone(str_replace("https://www.googleapis.com/compute/v1/projects/".$this->projectId."/zones/","",$instance->getZone()));
|
||||
$compute->setInternalIp($network->getNetworkIP());
|
||||
$compute->setExternalIp($accessConfig->getNatIP());
|
||||
$compute->setStatus("down");
|
||||
$this->entityManager->persist($compute);
|
||||
$instances[] = $compute;
|
||||
}
|
||||
}
|
||||
$this->entityManager->flush();
|
||||
return $instances;
|
||||
}
|
||||
|
||||
public function detail(Compute $compute)
|
||||
{
|
||||
$request = (new GetInstanceRequest())
|
||||
->setInstance($compute->getInstanceId())
|
||||
->setProject($this->projectId)
|
||||
->setZone($this->zone);
|
||||
$instance = $this->client->get($request);
|
||||
|
||||
$compute->setStatus($instance->getStatus());
|
||||
$compute->name = $instance->getName();
|
||||
return$compute;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,24 @@
|
||||
background-color: #4b5563;
|
||||
border-radius: 20px;
|
||||
}
|
||||
/* Hide submenu by default */
|
||||
.submenu {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.3s ease-out;
|
||||
}
|
||||
/* Show submenu when active */
|
||||
.submenu.active {
|
||||
max-height: 500px; /* A value large enough to contain all submenu items */
|
||||
transition: max-height 0.3s ease-in;
|
||||
}
|
||||
/* Rotate arrow icon */
|
||||
.arrow-icon {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
.arrow-icon.rotate {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
||||
@@ -45,6 +63,38 @@
|
||||
<span class="ml-3">Tableau de bord</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="px-4 py-2">
|
||||
<button class="flex items-center justify-between w-full p-2 text-base font-normal text-gray-900 dark:text-white rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none" data-submenu-toggle="infrastructure">
|
||||
<div class="flex items-center">
|
||||
<svg class="w-6 h-6 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path><path d="M12 2.252A8.014 8.014 0 0117.748 12H12V2.252z"></path></svg>
|
||||
<span class="ml-3">Infrastructure</span>
|
||||
</div>
|
||||
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400 arrow-icon" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
|
||||
</button>
|
||||
<ul id="submenu-infrastructure" class="submenu ml-6 mt-2 space-y-2">
|
||||
<li>
|
||||
<a href="{{ path('artemis_infrastructure_server') }}" class="flex items-center p-2 text-sm font-normal text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<span class="ml-3">Serveurs</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="px-4 py-2">
|
||||
<button class="flex items-center justify-between w-full p-2 text-base font-normal text-gray-900 dark:text-white rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none" data-submenu-toggle="intranet">
|
||||
<div class="flex items-center">
|
||||
<svg class="w-6 h-6 text-gray-500 dark:text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path><path d="M12 2.252A8.014 8.014 0 0117.748 12H12V2.252z"></path></svg>
|
||||
<span class="ml-3">Intranet</span>
|
||||
</div>
|
||||
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400 arrow-icon" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
|
||||
</button>
|
||||
<ul id="submenu-intranet" class="submenu ml-6 mt-2 space-y-2">
|
||||
<li>
|
||||
<a href="#" class="flex items-center p-2 text-sm font-normal text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<span class="ml-3">Serveurs</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
@@ -101,16 +151,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const sidebarToggle = document.getElementById('sidebar-toggle');
|
||||
|
||||
sidebarToggle.addEventListener('click', function () {
|
||||
sidebar.classList.toggle('-translate-x-full');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
37
templates/artemis/infra/server.twig
Normal file
37
templates/artemis/infra/server.twig
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends 'artemis/base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Liste des Serveurs</h2>
|
||||
<div>
|
||||
<button class="px-4 py-2 bg-blue-600 text-white font-medium rounded-md shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
+ Ajouter un nouveau serveur
|
||||
</button>
|
||||
{% if is_granted('ROLE_ROOT') %}
|
||||
<a href="{{ path('artemis_infrastructure_server',{sync:1}) }}" class="px-4 py-2 bg-purple-600 text-white font-medium rounded-md shadow-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900">
|
||||
Sync serveur
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
{% for instance in lists %}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-4 card-server">
|
||||
<div class="header flex justify-between items-center mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100">{{ instance.name }}</h3>
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-green-100 bg-{{ instance.status }}">{{ instance.status }}</span>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-500 dark:text-gray-300 mb-1">IP Interne: {{ instance.internalIp }}</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-300 mb-1">IP Externe: {{ instance.externalIp }}</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-300 mb-1">Sites: 5</p>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-300 mb-3">Uptime: 25 jours</p>
|
||||
<div class="flex justify-end space-x-2">
|
||||
<a href="#" class="text-blue-600 hover:text-blue-900 dark:text-blue-400 dark:hover:text-blue-200 text-sm">Voir</a>
|
||||
<a href="#" class="text-indigo-600 hover:text-indigo-900 dark:text-indigo-400 dark:hover:text-indigo-200 text-sm">Éditer</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user