aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--composer.json3
-rw-r--r--config/index.php4
-rw-r--r--public/js/maintenance.js4
-rw-r--r--src/controller/MaintenanceController.php19
-rw-r--r--src/service/Backup.php70
-rw-r--r--src/service/Installation.php47
-rw-r--r--src/service/router.php5
-rw-r--r--src/view/templates/form.php3
-rw-r--r--src/view/templates/maintenance.php12
10 files changed, 145 insertions, 23 deletions
diff --git a/.gitignore b/.gitignore
index 7b51759..2856821 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
1vendor/ 1vendor/
2var/
2config/config.ini 3config/config.ini
3public/js/tinymce 4public/js/tinymce
4public/js/tinymce-langs 5public/js/tinymce-langs
diff --git a/composer.json b/composer.json
index dd8aa9c..caf5a8c 100644
--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,8 @@
12 "symfony/http-foundation": "^7.3", 12 "symfony/http-foundation": "^7.3",
13 "twbs/bootstrap-icons": "^1.13", 13 "twbs/bootstrap-icons": "^1.13",
14 "symfony/var-exporter": "^7.0", 14 "symfony/var-exporter": "^7.0",
15 "mklkj/tinymce-i18n": "^25.11" 15 "mklkj/tinymce-i18n": "^25.11",
16 "symfony/process": "^8.0"
16 }, 17 },
17 "scripts": { 18 "scripts": {
18 "post-install-cmd": [ 19 "post-install-cmd": [
diff --git a/config/index.php b/config/index.php
new file mode 100644
index 0000000..f2fdd42
--- /dev/null
+++ b/config/index.php
@@ -0,0 +1,4 @@
1<?php
2// visite de config interdite lorsque la racine du serveur est visitable
3header('Location: ..');
4die; \ No newline at end of file
diff --git a/public/js/maintenance.js b/public/js/maintenance.js
index 84ca8e1..c12bd6b 100644
--- a/public/js/maintenance.js
+++ b/public/js/maintenance.js
@@ -7,7 +7,7 @@ function displayLogs(){
7 return; 7 return;
8 } 8 }
9 9
10 let fetcher = new Fetcher({ 10 const fetcher = new Fetcher({
11 endpoint: 'index.php?action=get_logs', 11 endpoint: 'index.php?action=get_logs',
12 method: 'POST', 12 method: 'POST',
13 onSuccess: (data) => { 13 onSuccess: (data) => {
@@ -26,7 +26,7 @@ function cleanLogs(){
26 } 26 }
27 const log_table = getElementOrThrow('log_table'); 27 const log_table = getElementOrThrow('log_table');
28 28
29 let fetcher = new Fetcher({ 29 const fetcher = new Fetcher({
30 endpoint: 'index.php?action=erase_logs', 30 endpoint: 'index.php?action=erase_logs',
31 method: 'POST', 31 method: 'POST',
32 onSuccess: () => { 32 onSuccess: () => {
diff --git a/src/controller/MaintenanceController.php b/src/controller/MaintenanceController.php
index 3b804fc..fca45f1 100644
--- a/src/controller/MaintenanceController.php
+++ b/src/controller/MaintenanceController.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
5 5
6use Doctrine\ORM\EntityManager; 6use Doctrine\ORM\EntityManager;
7use App\Entity\log; 7use App\Entity\log;
8use Symfony\Component\Process\Exception\ProcessFailedException;
8 9
9class MaintenanceController 10class MaintenanceController
10{ 11{
@@ -47,4 +48,22 @@ class MaintenanceController
47 } 48 }
48 die; 49 die;
49 } 50 }
51
52 static public function getLastDump(EntityManager $entityManager): void
53 {
54 try{
55 $file_path = Backup::mySQLdump($entityManager);
56 header('Content-Type: application/octet-stream'); // signifie fichier quelconque, du binaire quoi!
57 header('Content-Disposition: attachment; filename="' . basename($file_path) . '"'); // pour provoquer un téléchargement et non pour afficher
58 header('Content-Length: ' . filesize($file_path)); // peut servir côté client (barre de progression...)
59 readfile($file_path);
60 die;
61 }
62 // exeptions lancées dans Backup::mySQLdump
63 catch(ProcessFailedException $e){ // pas d'info $e pour le client
64 header('Location: ' . new URL(['page' => 'maintenance', 'error' => '500']));
65 die;
66 }
67 die;
68 }
50} \ No newline at end of file 69} \ No newline at end of file
diff --git a/src/service/Backup.php b/src/service/Backup.php
new file mode 100644
index 0000000..d628c27
--- /dev/null
+++ b/src/service/Backup.php
@@ -0,0 +1,70 @@
1<?php
2// src/service/Backup.php
3
4declare(strict_types=1);
5
6use Doctrine\ORM\EntityManager;
7use Symfony\Component\Process\Process; // protection injection dans le shell
8
9class Backup
10{
11 static private string $backup_dir = '../var/backups';
12 static private int $amount_to_keep = 30;
13
14 static public function mySQLdump(EntityManager $entityManager): string
15 {
16 $file_path = self::$backup_dir . '/db_' . Config::$database . '_' . new DateTime()->format('Y-m-d') . '.sql';
17
18 // les versions de mysql sont comme ci: 8.0.36
19 // celles de mariadb sont comme ça: 10.11.6-MariaDB
20 $version = $entityManager->getConnection()->fetchOne('SELECT VERSION()');
21 $engine = stripos($version, 'mariadb') !== false ? 'mariadb-dump' : 'mysqldump';
22
23 $tmp = tempnam('../var', 'tmp_db_codes_'); // crée un fichier avec un nom aléatoire et des droits 600 (concurrence)
24 file_put_contents($tmp,
25 "[client]\n
26 user=" . Config::$user . "\n
27 password=" . Config::$password . "\n
28 host=" . Config::$db_host . "\n");
29
30 $command = new Process([
31 $engine,
32 '--defaults-extra-file=' . $tmp, // pour ne pas enregistrer les codes dans l'historique de la console ou dans les processus de l'OS
33 '--single-transaction',
34 '--quick', // évite d'exploser la RAM si beaucoup de données
35 '--result-file=' . $file_path,
36 Config::$database
37 ]);
38
39 try{
40 $command->mustRun(); // comme run() mais lance une ProcessFailedException
41 return $file_path;
42 }
43 finally{
44 // exécuté même quand situé après "return"
45 unlink($tmp);
46 self::cleanBackups();
47 }
48
49 // compression gzip (gros gain de place sur le serveur), nécessite l'extension zlib
50 /*try{
51 file_put_contents(
52 $file_path . '.gz',
53 gzencode(file_get_contents($file_path), 5), // plus rapide que 9 et taille identique d'après mes essais
54 );
55 return $file_path . '.gz';
56 }
57 finally{
58 unlink($file_path);
59 }*/
60 }
61
62 static public function cleanBackups(): void {
63 $files = glob(self::$backup_dir . '/*.sql');
64 usort($files, fn($a, $b) => filemtime($b) <=> filemtime($a)); // filemtime = date de dernière modification
65 $files_to_delete = array_slice($files, self::$amount_to_keep);
66 foreach($files_to_delete as $file){
67 unlink($file);
68 }
69 }
70} \ No newline at end of file
diff --git a/src/service/Installation.php b/src/service/Installation.php
index 059c093..eb4b6db 100644
--- a/src/service/Installation.php
+++ b/src/service/Installation.php
@@ -9,10 +9,9 @@ class Installation
9 { 9 {
10 $flag = false; 10 $flag = false;
11 $extensions = ['pdo_mysql', 'mbstring', 'ctype', 'json', 'tokenizer', 'imagick']; // les 5 premières sont pour doctrine 11 $extensions = ['pdo_mysql', 'mbstring', 'ctype', 'json', 'tokenizer', 'imagick']; // les 5 premières sont pour doctrine
12 // ajouter plus tard zip pour les backup 12 // ajouter plus tard zlib pour la compression des backups
13 foreach($extensions as $extension){ 13 foreach($extensions as $extension){
14 if(!extension_loaded($extension)) 14 if(!extension_loaded($extension)){
15 {
16 echo("<p>l'extension <b>" . $extension . '</b> est manquante</p>'); 15 echo("<p>l'extension <b>" . $extension . '</b> est manquante</p>');
17 $flag = true; 16 $flag = true;
18 } 17 }
@@ -34,8 +33,8 @@ class Installation
34 static public function checkFilesAndFoldersRights(): void 33 static public function checkFilesAndFoldersRights(): void
35 { 34 {
36 // -- droits des fichiers et dossiers -- 35 // -- droits des fichiers et dossiers --
37 $droits_dossiers = 0700; 36 $droits_dossiers = 0755;
38 $droits_fichiers = 0600; 37 $droits_fichiers = 0644;
39 38
40 if(!file_exists('user_data')){ 39 if(!file_exists('user_data')){
41 // créer le dossier user_data 40 // créer le dossier user_data
@@ -46,25 +45,43 @@ class Installation
46 <p>Aide: "serveur web" se nomme "www-data" sur debian et ubuntu, il s\'appelera "http" sur d\'autres distributions.</p>'; 45 <p>Aide: "serveur web" se nomme "www-data" sur debian et ubuntu, il s\'appelera "http" sur d\'autres distributions.</p>';
47 die; 46 die;
48 } 47 }
48 if(!file_exists('../var')){
49 mkdir('../var');
50 chmod('../var', $droits_dossiers);
51 //
52 }
53 if(!file_exists('../var/backups')){
54 mkdir('../var/backups');
55 chmod('../var/backups', $droits_dossiers);
56 //
57 }
49 58
59 // droits 600 pour celui-ci
50 if(!file_exists('../config/config.ini')){ 60 if(!file_exists('../config/config.ini')){
51 // aide à la création du config.ini 61 // aide à la création du config.ini
52 echo '<p>Le fichier config/config.ini est introuvable.</p>'; 62 echo '<p>Le fichier config/config.ini est introuvable.</p>';
53 echo '<p>Il doit obligatoirement contenir les codes de la base de données, le protocole http ou https (et éventuellement le port) utilisé pour créer les liens internes.<br> 63 echo '<p>Il doit obligatoirement contenir les codes de la base de données, le protocole http ou https (et éventuellement le port) utilisé pour créer les liens internes.<br>
54 Un modèle est disponible, il s\'agit du fichier config/config-template.ini</p> 64 Un modèle est disponible, il s\'agit du fichier config/config-template.ini</p>
55 <p>Quand vous aurez terminé votre config.ini, donnez-lui par sécurité des droits 600.</p>'; 65 <p>Ce fichier a une importance critique. Si vous le pouvez faites en sorte que le serveur en soit le propriétaire et donner lui des droits 600.</p>';
56 die; 66 die;
57 } 67 }
58 else{ 68 /*else{
59 // droits du config.ini 69 // propriétaire du fichier
60 /*if(substr(sprintf('%o', fileperms('../config/config.ini')), -4) != 600){ 70 if(fileowner('../config/config.ini') != posix_geteuid()){
61 chmod('../config/config.ini', $droits_fichiers); 71 echo "<p>le fichier config/config.ini n'appartient pas au serveur.</p>";
62 }*/ 72 }
73 else{
74 // droits du config.ini
75 if(substr(sprintf('%o', fileperms('../config/config.ini')), -4) != 600){
76 echo '<p>Attention, le </p>';
77 //chmod('../config/config.ini', $droits_fichiers);
78 }
79 }
80 }*/
63 81
64 // tester les liens internes 82 // tester les liens internes
65 // 83 //
66 84
67 // le test de connexion à la BDD est dans le doctrine bootstrap 85 // le test de connexion à la BDD est dans le doctrine bootstrap
68 }
69 } 86 }
70} \ No newline at end of file 87} \ No newline at end of file
diff --git a/src/service/router.php b/src/service/router.php
index 508721c..6973656 100644
--- a/src/service/router.php
+++ b/src/service/router.php
@@ -45,7 +45,10 @@ if($request->getMethod() === 'GET'){
45 } 45 }
46 46
47 if(IS_ADMIN === true){ 47 if(IS_ADMIN === true){
48 // ... 48 if($request->query->has('action') && $request->query->get('action') === 'get_mysqldump'){
49 MaintenanceController::getLastDump($entityManager);
50 die;
51 }
49 } 52 }
50 53
51 // construction d'une page 54 // construction d'une page
diff --git a/src/view/templates/form.php b/src/view/templates/form.php
index b3611c1..384bde9 100644
--- a/src/view/templates/form.php
+++ b/src/view/templates/form.php
@@ -31,7 +31,6 @@
31 <p class="send_email_success_<?= $node->getNodeData()->getId() ?> full_width_column"></p> 31 <p class="send_email_success_<?= $node->getNodeData()->getId() ?> full_width_column"></p>
32 </div> 32 </div>
33 <p id="form_warning_<?= $node->getNodeData()->getId() ?>" class="form_warning <?= ($keep_emails ?? false) ? '' : 'hidden' ?>"><i> 33 <p id="form_warning_<?= $node->getNodeData()->getId() ?>" class="form_warning <?= ($keep_emails ?? false) ? '' : 'hidden' ?>"><i>
34 Une copie de votre e-mail (nom, adresse et message) sera conservée dans notre base de données dans le but de pouvoir répondre à votre demande et éventuellement dans un but de prospection. Ces données seront traitées automatiquement par notre serveur et conservées pendant au maximum <?= $retention_period ?> mois à compter de votre dernier message.<br> 34 Une copie de votre e-mail (nom, adresse et message) sera conservée dans notre base de données dans le but de pouvoir mieux répondre à votre demande et éventuellement dans d'autres buts (prospection). Ces données seront traitées automatiquement par notre serveur et conservées pendant au maximum <?= $retention_period ?> mois à compter de votre dernier message. Ce traitement repose sur votre consentement. Vous pouvez consulter, modifier ou supprimer vos données sur simple demande.
35 Ce traitement repose sur votre consentement. Vous pouvez consulter, modifier ou supprimer vos données en base de données sur simple demande.
36 </i></p> 35 </i></p>
37</section> \ No newline at end of file 36</section> \ No newline at end of file
diff --git a/src/view/templates/maintenance.php b/src/view/templates/maintenance.php
index 0118bbf..3501fa4 100644
--- a/src/view/templates/maintenance.php
+++ b/src/view/templates/maintenance.php
@@ -12,8 +12,16 @@
12 </div> 12 </div>
13 <div class="basic_div"> 13 <div class="basic_div">
14 <p> 14 <p>
15 <a href="<?= new URL(['page' => 'emails']) ?>"><button>Consulter les emails</button></a><br> 15 <a href="<?= new URL(['page' => 'emails']) ?>"><button>Consulter les emails</button></a>
16 <i>Emails reçus depuis tous les formulaires de contact</i> 16 <i>reçus depuis tous les formulaires de contact</i>
17 </p>
18 </div>
19 <div class="basic_div">
20 <p>
21 <a href="<?= new URL(['action' => 'get_mysqldump']) ?>">
22 <button id="get_mysqldump">Télécharger la base de données</button>
23 </a><br>
24 <i>Réalise un "mysqldump", vous obtiendrez un unique fichier contenant toute la BDD.</i>
17 </p> 25 </p>
18 </div> 26 </div>
19 27