false, 'error' => 'No autenticado']); exit; } // Permitir subida a cualquier usuario autenticado // Opcional: Restringir si es necesario // if (!hasPermission('editar_plantillas') && !hasPermission('crear_mensajes')) { ... } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); echo json_encode(['success' => false, 'error' => 'Método no permitido']); exit; } if (!isset($_FILES['image'])) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'No se envió ninguna imagen']); exit; } $file = $_FILES['image']; // Validar errores de subida if ($file['error'] !== UPLOAD_ERR_OK) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'Error al subir el archivo']); exit; } // Validar tipo de archivo $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $file['tmp_name']); finfo_close($finfo); if (!in_array($mimeType, $allowedTypes)) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'Tipo de archivo no permitido. Solo se aceptan JPG, PNG, GIF y WebP']); exit; } // Validar tamaño (máximo 5MB) if ($file['size'] > 5 * 1024 * 1024) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'El archivo es muy grande. Máximo 5MB']); exit; } try { $db = getDB(); // Calcular hash MD5 del archivo $hash = md5_file($file['tmp_name']); // Verificar si ya existe una imagen con el mismo hash $stmt = $db->prepare("SELECT * FROM gallery WHERE hash_md5 = ?"); $stmt->execute([$hash]); $existing = $stmt->fetch(); if ($existing) { // La imagen ya existe, devolver la existente echo json_encode([ 'success' => true, 'duplicate' => true, 'image' => [ 'id' => $existing['id'], 'nombre' => $existing['nombre'], 'ruta' => $existing['ruta'], 'ruta_thumbnail' => $existing['ruta_thumbnail'], 'url' => '/gallery/uploads/' . $existing['nombre'], 'url_thumbnail' => '/gallery/thumbnails/' . $existing['nombre'] ] ]); exit; } // Obtener dimensiones de la imagen $imageInfo = getimagesize($file['tmp_name']); $width = $imageInfo[0]; $height = $imageInfo[1]; // Generar nombre único para el archivo $extension = pathinfo($file['name'], PATHINFO_EXTENSION); $newName = time() . '_' . bin2hex(random_bytes(8)) . '.' . $extension; // Rutas de destino $uploadsDir = __DIR__ . '/../uploads/'; $thumbnailsDir = __DIR__ . '/../thumbnails/'; $uploadPath = $uploadsDir . $newName; $thumbnailPath = $thumbnailsDir . $newName; // Crear directorios si no existen if (!is_dir($uploadsDir)) { mkdir($uploadsDir, 0755, true); } if (!is_dir($thumbnailsDir)) { mkdir($thumbnailsDir, 0755, true); } // Mover archivo subido if (!move_uploaded_file($file['tmp_name'], $uploadPath)) { throw new Exception('Error al guardar el archivo'); } // Crear thumbnail createThumbnail($uploadPath, $thumbnailPath, 300, 300); // Guardar en la base de datos $stmt = $db->prepare(" INSERT INTO gallery (nombre, nombre_original, ruta, ruta_thumbnail, hash_md5, tipo_mime, tamano, ancho, alto, usuario_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "); $stmt->execute([ $newName, $file['name'], 'uploads/' . $newName, 'thumbnails/' . $newName, $hash, $mimeType, $file['size'], $width, $height, $userData->userId ]); $imageId = $db->lastInsertId(); echo json_encode([ 'success' => true, 'duplicate' => false, 'image' => [ 'id' => $imageId, 'nombre' => $newName, 'ruta' => 'uploads/' . $newName, 'ruta_thumbnail' => 'thumbnails/' . $newName, 'url' => '/gallery/uploads/' . $newName, 'url_thumbnail' => '/gallery/thumbnails/' . $newName ] ]); } catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => 'Error del servidor: ' . $e->getMessage()]); error_log('Error en upload.php: ' . $e->getMessage()); } /** * Crear thumbnail de una imagen */ function createThumbnail($sourcePath, $destPath, $maxWidth, $maxHeight) { $imageInfo = getimagesize($sourcePath); $width = $imageInfo[0]; $height = $imageInfo[1]; $mimeType = $imageInfo['mime']; // Cargar imagen según el tipo switch ($mimeType) { case 'image/jpeg': $source = imagecreatefromjpeg($sourcePath); break; case 'image/png': $source = imagecreatefrompng($sourcePath); break; case 'image/gif': $source = imagecreatefromgif($sourcePath); break; case 'image/webp': $source = imagecreatefromwebp($sourcePath); break; default: throw new Exception('Tipo de imagen no soportado'); } // Calcular nuevas dimensiones manteniendo el aspecto $ratio = min($maxWidth / $width, $maxHeight / $height); $newWidth = intval($width * $ratio); $newHeight = intval($height * $ratio); // Crear thumbnail $thumbnail = imagecreatetruecolor($newWidth, $newHeight); // Preservar transparencia para PNG y GIF if ($mimeType === 'image/png' || $mimeType === 'image/gif') { imagealphablending($thumbnail, false); imagesavealpha($thumbnail, true); $transparent = imagecolorallocatealpha($thumbnail, 255, 255, 255, 127); imagefilledrectangle($thumbnail, 0, 0, $newWidth, $newHeight, $transparent); } // Redimensionar imagecopyresampled($thumbnail, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); // Guardar thumbnail switch ($mimeType) { case 'image/jpeg': imagejpeg($thumbnail, $destPath, 85); break; case 'image/png': imagepng($thumbnail, $destPath, 8); break; case 'image/gif': imagegif($thumbnail, $destPath); break; case 'image/webp': imagewebp($thumbnail, $destPath, 85); break; } imagedestroy($source); imagedestroy($thumbnail); }