Les escribimos para informarles sobre un próximo cambio que podría afectar la forma en que acceden y utilizan los recursos de nuestra plataforma.
A partir del 09/04/2025, estaremos aplicando la política CORS en todas las URL de artefactos generados. Esto significa que todos los recursos alojados en nuestra plataforma requerirán un manejo adecuado para las solicitudes de origen cruzado, o en otras palabras, no podrán servir directamente los archivos de modelos generados utilizando el enlace que la API de Tripo les devuelve.
Este cambio es parte de nuestro compromiso para mejorar la seguridad y prevenir el acceso no autorizado a recursos a través de diferentes orígenes. CORS (Cross-Origin Resource Sharing) es un mecanismo que permite a los servidores especificar qué dominios externos pueden acceder a los recursos.
Para obtener más información sobre qué es CORS, pueden consultar la guía de MDN.
Actualmente, no proporcionamos encabezados CORS para las URL de artefactos. Como resultado, si están utilizando o enlazando a estos recursos directamente en un navegador o desde dominios externos, podrían experimentar problemas.
Para garantizar el acceso continuo a estos activos, deberán realizar una de las siguientes acciones:
La aplicación comienza el 09/04/2025.
¿Necesitan ejemplos de código prácticos? Si no están seguros de cómo empezar o encuentran el proceso difícil de implementar, hemos proporcionado fragmentos de código prácticos para ayudarles a comenzar. Consulten el Apéndice para más detalles.
¿Mi archivo generado se ha perdido?
No. Siempre pueden recuperar una nueva URL de la API en cualquier momento.
No devuelvo su URL directamente, pero realizo algún posprocesamiento. ¿Debería preocuparme?
Si no están devolviendo nuestra URL directamente a sus usuarios, pueden omitir este cambio. Sin embargo, les recomendamos monitorear su uso, especialmente si notan picos de error o comportamiento anormal en su sistema.
Si están utilizando FastAPI (que es fácil de portar o traducir a cualquier otro framework o lenguaje), así es como pueden ajustar su código existente para manejar CORS y guardar el artefacto localmente:
Código Actual:
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # La API funciona
return resp.json()['url']
Pueden modificarlo fácilmente a:
import httpx
import uuid
from fastapi.responses import FileResponse
from fastapi import HTTPException
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # igual que arriba
# Añadir el código de abajo
file_id = str(uuid.uuid4())
with httpx.Client() as client:
# Descargar el artefacto
response = client.get(url)
# Verificar si la solicitud fue exitosa
if response.status_code == 200:
# Luego re-guardarlo
with open(f"./downloaded/{file_id}.glb", "wb") as file:
file.write(response.content)
return file_id
@app.get("/artifact/{file_id}")
def download(file_id: str) -> FileResponse:
if os.path.exists(f"./downloaded/{file_id}.glb"):
return FileResponse(f"./downloaded/{file_id}.glb")
raise HTTPException(status_code=404, detail="Item not found")
Después de descargar y guardar el archivo localmente, deben servir a sus usuarios con la nueva URL. Por ejemplo, si su aplicación está alojada en https://app.example.com, la URL que deben servir se verá así:
https://app.example.com/artifact/<file_id>
Este fragmento de código es solo una demostración de cómo ajustar su integración. Sin embargo, hay algunas cosas importantes que deben considerar:
Si están desarrollando una Aplicación de Página Única (SPA) o un servidor Backend para Frontend (BFF) y prefieren no manejar las descargas y el almacenamiento de archivos directamente en su servidor, pueden usar un enfoque de proxy de borde. Esto les permite obtener el archivo de nuestro servidor, aplicar los encabezados CORS necesarios y luego servirlo a sus usuarios.
A continuación, se muestra un ejemplo de cómo implementar este enfoque usando Cloudflare Workers. Esta solución manejará los problemas de CORS descargando el archivo y reenviándolo bajo su dominio.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
// Analizar la URL de la solicitud
const url = new URL(request.url);
// Obtener la URL de destino del parámetro de consulta 'url'
const targetUrl = url.searchParams.get('url');
// Si no se proporciona una URL, devolver un error
if (!targetUrl) {
return new Response('Por favor, proporcione un parámetro de URL', {
status: 400,
headers: {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
}
});
}
// Manejar la solicitud OPTIONS previa al vuelo
if (request.method === 'OPTIONS') {
return handleCORS();
}
try {
// Obtener el archivo de la URL de destino
const response = await fetch(targetUrl);
// Si la obtención falló, devolver el error
if (!response.ok) {
return new Response(`Error al obtener de la URL de destino: ${response.statusText}`, {
status: response.status,
headers: corsHeaders()
});
}
// Obtener el tipo de contenido de la respuesta o por defecto octet-stream
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
// Obtener la disposición del contenido o crear una a partir de la URL
let contentDisposition = response.headers.get('Content-Disposition');
if (!contentDisposition) {
// Extraer el nombre de archivo de la URL
const fileName = targetUrl.split('/').pop().split('?')[0] || 'file';
contentDisposition = `attachment; filename="${fileName}"`;
}
// Crear una nueva respuesta con encabezados CORS
const newResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: {
'Content-Type': contentType,
'Content-Disposition': contentDisposition,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Cache-Control': 'public, max-age=3600' // Almacenar en caché durante 1 hora
}
});
return newResponse;
} catch (error) {
return new Response(`Error al obtener el archivo: ${error.message}`, {
status: 500,
headers: corsHeaders()
});
}
}
// Manejar las solicitudes previas al vuelo de CORS
function handleCORS() {
return new Response(null, {
status: 204, // Sin contenido
headers: corsHeaders()
});
}
// Crear objeto de encabezados CORS
function corsHeaders() {
return {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Max-Age': '86400' // 24 horas
};
}
Una vez que su Cloudflare Worker esté configurado, pueden integrarlo en su SPA utilizando el siguiente código JavaScript para obtener el archivo:
fetch('https://your-worker-url.workers.dev/?url=<target_url>')
.then(response => response.blob())
.then(blob => {
// Crear un enlace de descarga
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'downloaded-file.pdf';
document.body.appendChild(a);
a.click();
a.remove();
})
.catch(error => console.error('Error:', error));
Esta solución es perfecta para SPAs o servidores BFF que necesitan evitar las restricciones CORS al descargar y servir archivos a los usuarios finales. El archivo se obtiene de la URL de destino, se pasa a través del proxy de borde y se sirve con los encabezados CORS necesarios.
Si prefieren configurar un proxy CORS simple, pueden consultar este ejemplo proporcionado por Cloudflare: Cloudflare Workers - CORS Header Proxy
Esto les permitirá manejar las solicitudes de origen cruzado con mayor facilidad, mientras siguen cumpliendo con las políticas CORS.
moving at the speed of creativity, achieving the depths of imagination.