feat: Release v3.1.6 - Amélioration complète des flux de passages
- Optimisation des listes de passages (user/admin) - Amélioration du flux de création avec validation temps réel - Amélioration du flux de consultation avec export multi-formats - Amélioration du flux de modification avec suivi des changements - Ajout de la génération PDF pour les reçus - Migration de la structure des uploads - Implémentation de la file d'attente d'emails - Ajout des permissions de suppression de passages - Corrections de bugs et optimisations performances 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
298
api/scripts/migrate_uploads_structure.php
Normal file
298
api/scripts/migrate_uploads_structure.php
Normal file
@@ -0,0 +1,298 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Script de migration de l'arborescence des uploads
|
||||
* Réorganise les fichiers existants vers la nouvelle structure simplifiée
|
||||
*
|
||||
* Ancienne structure : uploads/entites/{id}/* et uploads/{id}/*
|
||||
* Nouvelle structure : uploads/{id}/*
|
||||
*
|
||||
* Usage: php scripts/migrate_uploads_structure.php [--dry-run]
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// Chemin de base des uploads
|
||||
const BASE_PATH = '/var/www/geosector/api/uploads';
|
||||
const LOG_FILE = '/var/www/geosector/api/logs/migration_uploads_' . date('Ymd_His') . '.log';
|
||||
|
||||
// Mode dry-run (simulation sans modification)
|
||||
$dryRun = in_array('--dry-run', $argv);
|
||||
|
||||
// Fonction pour logger
|
||||
function logMessage(string $message, string $level = 'INFO'): void {
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$log = "[$timestamp] [$level] $message" . PHP_EOL;
|
||||
echo $log;
|
||||
if (!$GLOBALS['dryRun']) {
|
||||
file_put_contents(LOG_FILE, $log, FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction pour déplacer un fichier ou dossier
|
||||
function moveItem(string $source, string $destination): bool {
|
||||
global $dryRun;
|
||||
|
||||
if (!file_exists($source)) {
|
||||
logMessage("Source n'existe pas: $source", 'WARNING');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Créer le dossier de destination si nécessaire
|
||||
$destDir = dirname($destination);
|
||||
if (!is_dir($destDir)) {
|
||||
logMessage("Création du dossier: $destDir");
|
||||
if (!$dryRun) {
|
||||
mkdir($destDir, 0775, true);
|
||||
chown($destDir, 'nginx');
|
||||
chgrp($destDir, 'nobody');
|
||||
}
|
||||
}
|
||||
|
||||
// Déplacer l'élément
|
||||
logMessage("Déplacement: $source -> $destination");
|
||||
if (!$dryRun) {
|
||||
if (is_dir($source)) {
|
||||
// Pour un dossier, utiliser rename
|
||||
return rename($source, $destination);
|
||||
} else {
|
||||
// Pour un fichier
|
||||
return rename($source, $destination);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fonction pour copier récursivement un dossier
|
||||
function copyDirectory(string $source, string $dest): bool {
|
||||
global $dryRun;
|
||||
|
||||
if (!is_dir($source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$dryRun) {
|
||||
if (!is_dir($dest)) {
|
||||
mkdir($dest, 0775, true);
|
||||
chown($dest, 'nginx');
|
||||
chgrp($dest, 'nobody');
|
||||
}
|
||||
}
|
||||
|
||||
$dir = opendir($source);
|
||||
while (($file = readdir($dir)) !== false) {
|
||||
if ($file === '.' || $file === '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$srcPath = "$source/$file";
|
||||
$destPath = "$dest/$file";
|
||||
|
||||
if (is_dir($srcPath)) {
|
||||
copyDirectory($srcPath, $destPath);
|
||||
} else {
|
||||
logMessage("Copie: $srcPath -> $destPath");
|
||||
if (!$dryRun) {
|
||||
copy($srcPath, $destPath);
|
||||
chmod($destPath, 0664);
|
||||
chown($destPath, 'nginx');
|
||||
chgrp($destPath, 'nobody');
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fonction principale de migration
|
||||
function migrateUploads(): void {
|
||||
global $dryRun;
|
||||
|
||||
logMessage("=== Début de la migration des uploads ===");
|
||||
logMessage($dryRun ? "MODE DRY-RUN (simulation)" : "MODE RÉEL (modifications effectives)");
|
||||
|
||||
// 1. Migrer uploads/entites/* vers uploads/*
|
||||
$entitesPath = BASE_PATH . '/entites';
|
||||
if (is_dir($entitesPath)) {
|
||||
logMessage("Traitement du dossier entites/");
|
||||
|
||||
$entites = scandir($entitesPath);
|
||||
foreach ($entites as $entiteId) {
|
||||
if ($entiteId === '.' || $entiteId === '..') continue;
|
||||
|
||||
$oldPath = "$entitesPath/$entiteId";
|
||||
$newPath = BASE_PATH . "/$entiteId";
|
||||
|
||||
if (!is_dir($oldPath)) continue;
|
||||
|
||||
logMessage("Migration entité $entiteId");
|
||||
|
||||
// Si le dossier destination existe déjà, fusionner
|
||||
if (is_dir($newPath)) {
|
||||
logMessage("Le dossier $entiteId existe déjà à la racine, fusion nécessaire", 'INFO');
|
||||
|
||||
// Migrer les sous-dossiers
|
||||
$subDirs = scandir($oldPath);
|
||||
foreach ($subDirs as $subDir) {
|
||||
if ($subDir === '.' || $subDir === '..') continue;
|
||||
|
||||
$oldSubPath = "$oldPath/$subDir";
|
||||
$newSubPath = "$newPath/$subDir";
|
||||
|
||||
if ($subDir === 'operations') {
|
||||
// Traiter spécialement le dossier operations
|
||||
migrateOperations($oldSubPath, $newSubPath);
|
||||
} else {
|
||||
// Pour logo et recus, déplacer directement
|
||||
if (!is_dir($newSubPath)) {
|
||||
moveItem($oldSubPath, $newSubPath);
|
||||
} else {
|
||||
logMessage("Le dossier $newSubPath existe déjà, fusion du contenu");
|
||||
copyDirectory($oldSubPath, $newSubPath);
|
||||
if (!$dryRun) {
|
||||
// Supprimer l'ancien après copie
|
||||
exec("rm -rf " . escapeshellarg($oldSubPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Déplacer simplement le dossier entier
|
||||
moveItem($oldPath, $newPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Supprimer le dossier entites vide
|
||||
if (!$dryRun) {
|
||||
if (count(scandir($entitesPath)) === 2) { // Seulement . et ..
|
||||
rmdir($entitesPath);
|
||||
logMessage("Suppression du dossier entites/ vide");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Nettoyer la structure des dossiers operations
|
||||
logMessage("Nettoyage de la structure des dossiers operations");
|
||||
cleanupOperationsStructure();
|
||||
|
||||
logMessage("=== Migration terminée ===");
|
||||
if (!$dryRun) {
|
||||
logMessage("Logs sauvegardés dans: " . LOG_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction pour migrer le dossier operations avec simplification
|
||||
function migrateOperations(string $oldPath, string $newPath): void {
|
||||
global $dryRun;
|
||||
|
||||
if (!is_dir($oldPath)) return;
|
||||
|
||||
logMessage("Migration du dossier operations: $oldPath");
|
||||
|
||||
if (!$dryRun && !is_dir($newPath)) {
|
||||
mkdir($newPath, 0775, true);
|
||||
chown($newPath, 'nginx');
|
||||
chgrp($newPath, 'nobody');
|
||||
}
|
||||
|
||||
$operations = scandir($oldPath);
|
||||
foreach ($operations as $opId) {
|
||||
if ($opId === '.' || $opId === '..') continue;
|
||||
|
||||
$oldOpPath = "$oldPath/$opId";
|
||||
$newOpPath = "$newPath/$opId";
|
||||
|
||||
// Simplifier la structure: déplacer les xlsx directement dans operations/{id}/
|
||||
if (is_dir("$oldOpPath/documents/exports/excel")) {
|
||||
$excelPath = "$oldOpPath/documents/exports/excel";
|
||||
$files = scandir($excelPath);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file === '.' || $file === '..' || !str_ends_with($file, '.xlsx')) continue;
|
||||
|
||||
$oldFilePath = "$excelPath/$file";
|
||||
$newFilePath = "$newOpPath/$file";
|
||||
|
||||
logMessage("Déplacement Excel: $oldFilePath -> $newFilePath");
|
||||
|
||||
if (!$dryRun) {
|
||||
if (!is_dir($newOpPath)) {
|
||||
mkdir($newOpPath, 0775, true);
|
||||
chown($newOpPath, 'nginx');
|
||||
chgrp($newOpPath, 'nobody');
|
||||
}
|
||||
rename($oldFilePath, $newFilePath);
|
||||
chmod($newFilePath, 0664);
|
||||
chown($newFilePath, 'nginx');
|
||||
chgrp($newFilePath, 'nobody');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction pour nettoyer la structure après migration
|
||||
function cleanupOperationsStructure(): void {
|
||||
global $dryRun;
|
||||
|
||||
$uploadsDir = BASE_PATH;
|
||||
$entites = scandir($uploadsDir);
|
||||
|
||||
foreach ($entites as $entiteId) {
|
||||
if ($entiteId === '.' || $entiteId === '..' || $entiteId === 'entites') continue;
|
||||
|
||||
$operationsPath = "$uploadsDir/$entiteId/operations";
|
||||
if (!is_dir($operationsPath)) continue;
|
||||
|
||||
$operations = scandir($operationsPath);
|
||||
foreach ($operations as $opId) {
|
||||
if ($opId === '.' || $opId === '..') continue;
|
||||
|
||||
$opPath = "$operationsPath/$opId";
|
||||
|
||||
// Supprimer l'ancienne structure documents/exports/excel si elle est vide
|
||||
$oldStructure = "$opPath/documents";
|
||||
if (is_dir($oldStructure)) {
|
||||
logMessage("Suppression de l'ancienne structure: $oldStructure");
|
||||
if (!$dryRun) {
|
||||
exec("rm -rf " . escapeshellarg($oldStructure));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier les permissions
|
||||
if (!is_dir(BASE_PATH)) {
|
||||
die("ERREUR: Le dossier " . BASE_PATH . " n'existe pas\n");
|
||||
}
|
||||
|
||||
if (!is_writable(BASE_PATH) && !$dryRun) {
|
||||
die("ERREUR: Le dossier " . BASE_PATH . " n'est pas accessible en écriture\n");
|
||||
}
|
||||
|
||||
// Lancer la migration
|
||||
try {
|
||||
migrateUploads();
|
||||
|
||||
if ($dryRun) {
|
||||
echo "\n";
|
||||
echo "========================================\n";
|
||||
echo "SIMULATION TERMINÉE\n";
|
||||
echo "Pour exécuter réellement la migration:\n";
|
||||
echo "php " . $argv[0] . "\n";
|
||||
echo "========================================\n";
|
||||
} else {
|
||||
echo "\n";
|
||||
echo "========================================\n";
|
||||
echo "MIGRATION TERMINÉE AVEC SUCCÈS\n";
|
||||
echo "Vérifiez les logs: " . LOG_FILE . "\n";
|
||||
echo "========================================\n";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
logMessage("ERREUR FATALE: " . $e->getMessage(), 'ERROR');
|
||||
exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user