164 lines
5.6 KiB
PHP
164 lines
5.6 KiB
PHP
<?php
|
|
/**
|
|
* Endpoint: /api/version/media-url.php
|
|
* Parámetros: id (media id) o url (direct url), opcional download=1 para proxy y forzar descarga
|
|
* - Si se solicita JSON (Accept: application/json) la endpoint requiere autenticación y devuelve {success:true,url:...}
|
|
* - Si se accede mediante GET desde navegador, redirige (302) al URL del media (no requiere sesión)
|
|
*/
|
|
require_once __DIR__ . '/../../config/config.php';
|
|
|
|
$isGet = $_SERVER['REQUEST_METHOD'] === 'GET';
|
|
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
|
|
$wantsJson = strpos($accept, 'application/json') !== false;
|
|
|
|
// Requerir autenticación sólo para peticiones no-GET o cuando se solicita JSON (API/AJAX)
|
|
if (!$isGet || $wantsJson) {
|
|
requireAuthentication();
|
|
}
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
$mediaId = $_GET['id'] ?? $_POST['id'] ?? null;
|
|
$providedUrl = $_GET['url'] ?? $_POST['url'] ?? null;
|
|
$download = isset($_GET['download']) && $_GET['download'] === '1';
|
|
|
|
if (!$mediaId && !$providedUrl) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'error' => 'Se requiere id o url']);
|
|
exit;
|
|
}
|
|
|
|
$mediaUrl = null;
|
|
|
|
if ($providedUrl) {
|
|
$mediaUrl = $providedUrl;
|
|
} else {
|
|
$token = getConfigFromDB('whatsapp_token', '');
|
|
$apiUrl = rtrim(getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), '/');
|
|
$endpoint = "{$apiUrl}/{$mediaId}";
|
|
|
|
$ch = curl_init();
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_URL => $endpoint,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $token ],
|
|
CURLOPT_TIMEOUT => 15,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$err = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($err) {
|
|
http_response_code(502);
|
|
echo json_encode(['success' => false, 'error' => 'Error comunicando con Graph API', 'detail' => $err]);
|
|
exit;
|
|
}
|
|
|
|
$decoded = json_decode($response, true);
|
|
if ($httpCode >= 400 || !$decoded) {
|
|
http_response_code($httpCode ?: 500);
|
|
echo json_encode(['success' => false, 'error' => 'Graph API error', 'response' => $decoded]);
|
|
exit;
|
|
}
|
|
|
|
$mediaUrl = $decoded['url'] ?? ($decoded['data'][0]['url'] ?? null);
|
|
if (!$mediaUrl) {
|
|
http_response_code(404);
|
|
echo json_encode(['success' => false, 'error' => 'No se recibió URL del media', 'raw' => $decoded]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// Soporte para archivos locales (local=uploads/media/...)
|
|
$local = $_GET['local'] ?? null;
|
|
if ($local) {
|
|
$localPath = realpath(__DIR__ . '/../../' . ltrim($local, '/'));
|
|
$base = realpath(__DIR__ . '/../../uploads/media');
|
|
// Prevención simple de path traversal
|
|
if (!$localPath || strpos($localPath, $base) !== 0) {
|
|
http_response_code(400);
|
|
echo json_encode(['success' => false, 'error' => 'Ruta local inválida']);
|
|
exit;
|
|
}
|
|
|
|
if (!file_exists($localPath)) {
|
|
http_response_code(404);
|
|
echo json_encode(['success' => false, 'error' => 'Archivo no encontrado']);
|
|
exit;
|
|
}
|
|
|
|
if ($wantsJson) {
|
|
// devolver URL pública directa (relative)
|
|
echo json_encode(['success' => true, 'url' => '/' . ltrim($local, '/')]);
|
|
exit;
|
|
}
|
|
|
|
if ($download) {
|
|
// Forzar descarga del archivo local
|
|
header('Cache-Control: public, max-age=3600');
|
|
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
$ctype = finfo_file($finfo, $localPath);
|
|
finfo_close($finfo);
|
|
header('Content-Type: ' . $ctype);
|
|
header('Content-Length: ' . filesize($localPath));
|
|
header('Content-Disposition: attachment; filename="' . basename($localPath) . '"');
|
|
readfile($localPath);
|
|
exit;
|
|
}
|
|
|
|
// Redirigir al archivo local (servidor web lo servirá)
|
|
header('Cache-Control: public, max-age=3600');
|
|
header('Location: /' . ltrim($local, '/'), true, 302);
|
|
exit;
|
|
}
|
|
|
|
// Si se solicitó JSON, devolver la URL
|
|
if ($wantsJson) {
|
|
echo json_encode(['success' => true, 'url' => $mediaUrl]);
|
|
exit;
|
|
}
|
|
|
|
// Si se pidió proxy (download=1), traer el contenido y retornarlo como descarga
|
|
if ($download) {
|
|
$ch = curl_init();
|
|
// Añadir Authorization si existe token en configuración
|
|
$token = getConfigFromDB('whatsapp_token', '');
|
|
$headers = ['User-Agent: WhatsAppMediaProxy/1.0', 'Accept: */*'];
|
|
if ($token) $headers[] = 'Authorization: Bearer ' . $token;
|
|
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_URL => $mediaUrl,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_TIMEOUT => 60,
|
|
CURLOPT_HTTPHEADER => $headers,
|
|
]);
|
|
$content = curl_exec($ch);
|
|
$info = curl_getinfo($ch);
|
|
$err = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($err) {
|
|
http_response_code(502);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode(['success' => false, 'error' => 'Error al descargar el archivo', 'detail' => $err]);
|
|
exit;
|
|
}
|
|
|
|
// Cabeceras para descarga
|
|
header_remove('Content-Type');
|
|
header('Cache-Control: public, max-age=3600');
|
|
if (!empty($info['content_type'])) header('Content-Type: ' . $info['content_type']);
|
|
if (!empty($info['size_download'])) header('Content-Length: ' . $info['size_download']);
|
|
$filename = basename(parse_url($mediaUrl, PHP_URL_PATH) ?: 'file');
|
|
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
|
echo $content;
|
|
exit;
|
|
}
|
|
|
|
// Por defecto redirigir al URL (navegador hará la descarga o mostrará la imagen)
|
|
header('Cache-Control: public, max-age=3600');
|
|
header('Location: ' . $mediaUrl, true, 302);
|
|
exit;
|