feat(BackupCommand): Ajoute la commande de sauvegarde de la base de données et des uploads.

This commit is contained in:
Serreau Jovann
2025-09-25 09:31:23 +02:00
parent ea0ce9808f
commit a5333199e9
7 changed files with 143 additions and 6 deletions

2
.gitignore vendored
View File

@@ -33,3 +33,5 @@ coverage/
.phpunit.cache
/public/build
script/demande/hosts.ini
backup/*.zip
backup/*.sql

View File

@@ -1,2 +1,2 @@
[webservers]
127.0.0.1 ansible_connection=local ansible_python_interpreter=/usr/bin/python3 path=/var/www/mainframe/app
127.0.0.1 ansible_connection=local ansible_python_interpreter=/usr/bin/python3 path=/var/www/mainframe/public

0
backup/.gitignore vendored Normal file
View File

View File

@@ -72,7 +72,8 @@
"twig/extra-bundle": "^3.21",
"twig/intl-extra": "^3.21",
"twig/twig": "^3.21",
"vich/uploader-bundle": "^2.7"
"vich/uploader-bundle": "^2.7",
"ext-zip": "*"
},
"config": {
"allow-plugins": {

View File

@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Entity\Revendeur;
use App\Repository\RevendeurRepository;
use App\Service\Revendeur\RevendeurService;
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\Finder\Finder;
use Symfony\Component\HttpKernel\KernelInterface;
#[AsCommand(name: 'mainframe:backup', description: 'Backup command')]
class BackupCommand extends Command
{
public function __construct(
private readonly KernelInterface $kernelInterface,
private readonly RevendeurService $revendeurService,
?string $name = null)
{
parent::__construct($name);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$io->title('Backup command');
$path = $this->kernelInterface->getProjectDir()."/backup";
$this->backuped($path);
$finder = new Finder();
$files = $finder->in($path)->files()->name('*.tar.gz');
$now = time();
foreach ($files as $file) {
if ($now - $file->getMTime() > 60 * 60 * 24 * 7) {
}
}
return Command::SUCCESS;
}
private function backuped(string $backupPath): void
{
$database= $_ENV['DATABASE_URL'];
preg_match(
'/^postgres(?:ql)?:\\/\\/(.*?):(.*?)@(.*?):(\\d+)\\/(.*?)(?:\\?|$)/',
$database,
$matches
);
// À adapter selon ta configuration/env Symfony !
$user = $matches[1] ?? null;
$password = $matches[2] ?? null;
$host = $matches[3] ?? null;
$port = $matches[4] ?? null;
$db = $matches[5] ?? null;
$sqlFilename = sprintf('%s/pgsql_backup_%s.sql', $backupPath, date('Y-m-d_His'));
// Pour éviter les exposes de mdp dans commande : passer par env
putenv("PGPASSWORD={$password}");
// Option -F c pour backup custom compressé, -f pour destination
$command = sprintf(
'pg_dump -h %s -U %s -F c %s -f %s',
$host,
$user,
$db,
$sqlFilename
);
// Exécution du backup
exec($command);
$zipFilename = sprintf('%s/backup_%s.zip', $backupPath, date('d-m-Y'));
// Création du ZIP et ajout du SQL dedans
$zip = new \ZipArchive();
if ($zip->open($zipFilename, \ZipArchive::CREATE) === true) {
// Le fichier dans le zip portera juste le nom du .sql
$zip->addFile($sqlFilename, basename($sqlFilename));
$vichUploadDirs = [
$this->kernelInterface->getProjectDir() . '/public/storage', // Exemple chemin upload
// ajouter dautres chemins si besoin
];
foreach ($vichUploadDirs as $dir) {
$this->addDirToZip($zip, $dir, basename($dir));
}
$zip->close();
unlink($sqlFilename);
}
}
private function addDirToZip(\ZipArchive $zip, string $folder, string $zipPath): void
{
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($folder),
\RecursiveIteratorIterator::LEAVES_ONLY
);
foreach ($files as $name => $file) {
if (!$file->isDir()) {
$filePath = $file->getRealPath();
$relativePath = $zipPath . '/' . substr($filePath, strlen($folder) + 1);
$zip->addFile($filePath, $relativePath);
}
}
}
}

View File

@@ -7,6 +7,8 @@ use App\Form\Artemis\Revendeur\RevendeurType;
use App\Service\Revendeur\RevendeurService;
use App\Service\Revendeur\SendLinkEvent;
use Cocur\Slugify\Slugify;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -16,11 +18,17 @@ use Symfony\Component\Routing\Attribute\Route;
class RevendeurController extends AbstractController
{
#[Route(path: '/artemis/revendeur',name: 'artemis_revendeur')]
public function revendeur(Request $request,EventDispatcherInterface $eventDispatcher,RevendeurService $revendeurService): Response
public function revendeur(EntityManagerInterface $entityManager,Request $request,EventDispatcherInterface $eventDispatcher,RevendeurService $revendeurService): Response
{
if($request->query->has('delete')) {
$revendeur = $revendeurService->get($request->query->get('id'));
$entityManager->remove($revendeur);
$entityManager->flush();
$this->addFlash("success","Revendeur supprimer");
return $this->redirectToRoute('artemis_revendeur');
}
if($request->query->has('sendLink')) {
$revendeur = $revendeurService->get($request->query->get('id'));
$dns = $revendeur->getCode() . "-demande.esy-web.fr";
$eventSendLink = new SendLinkEvent($revendeur);
$eventDispatcher->dispatch($eventSendLink);
$this->addFlash("success","Le lien à été envoyée");
@@ -31,6 +39,11 @@ class RevendeurController extends AbstractController
'revendeurLists' => $revendeurService->list(),
]);
}
#[Route(path: '/artemis/revendeur/{id}',name: 'artemis_revendeur_view')]
public function revendeurVideo(EntityManagerInterface $entityManager,Request $request,EventDispatcherInterface $eventDispatcher,RevendeurService $revendeurService): Response
{
}
#[Route(path: '/artemis/revendeur/add',name: 'artemis_revendeur_add')]
public function revendeurAdd(Request $request,RevendeurService $revendeurService): Response

View File

@@ -26,9 +26,9 @@
<td class="px-6 py-4 text-center text-sm">{{ revendeur.name }} {{ revendeur.surname }}</td>
<td class="px-6 py-4 text-center text-sm">{{ revendeur.email }} {{ revendeur.phone }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-center">
<a href="{{ path('artemis_revendeur',{id:revendeur.id}) }}&sendLink=1" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded mr-2">Modifier</a>
<a href="{{ path('artemis_revendeur_view',{id:revendeur.id}) }}" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded mr-2">Modifier</a>
<a href="{{ path('artemis_revendeur',{id:revendeur.id}) }}&sendLink=1" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded mr-2">Envoyée le lien du revendeur</a>
<a target="_blank" href="{{ path('artemis_revendeur',{id:revendeur.id}) }}?open=1" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded mr-2">Voir le lien du revendeur</a>
<a target="_blank" href="https://{{ revendeur.code }}-demande.esy-web.fr" class="bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded mr-2">Voir le lien du revendeur</a>
<a href="{{ path('artemis_revendeur',{id:revendeur.id}) }}?delete=1" class="bg-red-600 hover:bg-red-700 text-white px-3 py-1 rounded mr-2">Supprimer</a>
</td>
</tr>