Initial commit: FuZip - Application de fusion interactive de fichiers ZIP

- Backend PHP: architecture MVC avec API REST (upload, merge, preview, extract)
  - Frontend JavaScript: composants modulaires (arborescence, upload, themes, i18n)
  - Fonctionnalités: drag&drop, sélection exclusive, détection conflits, persistance état
  - Sécurité: validation stricte, isolation sessions, sanitization chemins
  - UI/UX: responsive, thèmes clair/sombre, multi-langue (FR/EN)
  - Documentation: README complet avec installation et utilisation
This commit is contained in:
2026-01-12 03:29:01 +01:00
commit bd6d321ed7
24 changed files with 6463 additions and 0 deletions

190
core/Config.php Normal file
View File

@@ -0,0 +1,190 @@
<?php
/**
* Configuration globale de FuZip
*
* Ce fichier contient toutes les constantes de configuration
* pour l'application de fusion de fichiers ZIP.
*/
class Config {
/**
* Taille maximale d'un fichier ZIP en octets (500 MB)
* Modifié selon les besoins spécifiés dans le plan
*/
const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500 MB
/**
* Durée de vie d'une session en secondes (24 heures)
* Après ce délai, les dossiers de session sont supprimés
*/
const SESSION_LIFETIME = 24 * 3600; // 24 heures
/**
* Chemin absolu vers le dossier uploads
* Utilise DIRECTORY_SEPARATOR pour compatibilité multi-plateforme
*/
const UPLOAD_DIR = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;
/**
* Extensions de fichiers interdites dans les ZIP
* Pour des raisons de sécurité
*/
const FORBIDDEN_EXTENSIONS = [
'exe', 'bat', 'sh', 'ps1', 'php', 'phtml',
'php3', 'php4', 'php5', 'phps', 'cgi'
];
/**
* Types MIME autorisés pour les uploads
*/
const ALLOWED_MIME_TYPES = [
'application/zip',
'application/x-zip',
'application/x-zip-compressed',
'application/octet-stream' // Parfois utilisé pour les ZIP
];
/**
* Signatures magiques des fichiers ZIP (magic bytes)
* Pour vérifier l'authenticité du fichier
*/
const ZIP_MAGIC_BYTES = [
'504B0304', // PK.. (ZIP standard)
'504B0506', // PK.. (ZIP vide)
'504B0708' // PK.. (ZIP spanned)
];
/**
* Nombre maximum de fichiers par ZIP
* Pour éviter les problèmes de performance
*/
const MAX_FILES_PER_ZIP = 10000;
/**
* Profondeur maximale de l'arborescence
* Pour éviter les problèmes de récursion
*/
const MAX_TREE_DEPTH = 50;
/**
* Timeout pour le traitement d'un ZIP (en secondes)
*/
const PROCESSING_TIMEOUT = 300; // 5 minutes
/**
* Taille du buffer pour le streaming de fichiers
*/
const STREAM_BUFFER_SIZE = 8192; // 8 KB
/**
* Langues supportées
*/
const SUPPORTED_LANGUAGES = ['fr', 'en'];
/**
* Langue par défaut
*/
const DEFAULT_LANGUAGE = 'fr';
/**
* Mode debug (activer les logs détaillés)
* À désactiver en production
*/
const DEBUG_MODE = true;
/**
* Chemin vers le fichier de log
*/
const LOG_FILE = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'fuzip_debug.log';
/**
* Obtient le chemin complet normalisé du dossier uploads
*
* @return string Chemin absolu vers uploads/
*/
public static function getUploadDir(): string {
$path = realpath(self::UPLOAD_DIR);
if ($path === false) {
// Si le dossier n'existe pas encore, le créer
if (!file_exists(self::UPLOAD_DIR)) {
mkdir(self::UPLOAD_DIR, 0755, true);
}
$path = realpath(self::UPLOAD_DIR);
}
return $path . DIRECTORY_SEPARATOR;
}
/**
* Vérifie si une extension de fichier est interdite
*
* @param string $filename Nom du fichier
* @return bool True si l'extension est interdite
*/
public static function isForbiddenExtension(string $filename): bool {
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return in_array($extension, self::FORBIDDEN_EXTENSIONS);
}
/**
* Vérifie si un type MIME est autorisé
*
* @param string $mimeType Type MIME à vérifier
* @return bool True si le type MIME est autorisé
*/
public static function isAllowedMimeType(string $mimeType): bool {
return in_array($mimeType, self::ALLOWED_MIME_TYPES);
}
/**
* Log un message (si DEBUG_MODE activé)
*
* @param string $message Message à logger
* @param string $level Niveau (INFO, WARNING, ERROR)
*/
public static function log(string $message, string $level = 'INFO'): void {
if (!self::DEBUG_MODE) {
return;
}
$timestamp = date('Y-m-d H:i:s');
$logMessage = "[{$timestamp}] [{$level}] {$message}" . PHP_EOL;
file_put_contents(self::LOG_FILE, $logMessage, FILE_APPEND);
}
/**
* Convertit une taille en octets en format lisible
*
* @param int $bytes Taille en octets
* @return string Taille formatée (ex: "2.5 MB")
*/
public static function formatBytes(int $bytes): string {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, 2) . ' ' . $units[$pow];
}
/**
* Sanitize un nom de fichier/dossier
* Retire les caractères dangereux et les séquences ..
*
* @param string $name Nom à sanitizer
* @return string Nom nettoyé
*/
public static function sanitizePath(string $name): string {
// Retirer les séquences dangereuses
$name = str_replace(['..', '\\', chr(0)], '', $name);
// Garder seulement les caractères alphanumériques, espaces, tirets, underscores, points et slashes
$name = preg_replace('/[^a-zA-Z0-9\s\-_\.\/]/', '', $name);
// Retirer les slashes multiples
$name = preg_replace('#/+#', '/', $name);
return trim($name);
}
}