Les escribimos para informarles sobre un próximo cambio que puede afectar la forma en que acceden y utilizan los recursos de nuestra plataforma.
Implementaremos una política CORS obligatoria 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 archivos de modelos generados utilizando el enlace que les devuelve la API de Tripo.
Este cambio forma parte de nuestro compromiso de mejorar la seguridad y prevenir el acceso no autorizado a los recursos en 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 los artefactos. Como resultado, si están utilizando o enlazando directamente a estos recursos en un navegador o desde dominios externos, pueden experimentar problemas.
Para asegurar el acceso continuo a estos activos, deberán tomar una de las siguientes acciones:
¿Necesitan ejemplos de código prácticos? Si no están seguros de cómo empezar o el proceso les resulta difícil de implementar, hemos proporcionado fragmentos de código prácticos para ayudarles a comenzar. Por favor, consulten el Apéndice para más detalles.
¿Se ha perdido mi archivo generado?
No. Siempre pueden recuperar una nueva URL de la API en cualquier momento.
No devuelvo su URL directamente, pero hago algún post-procesamiento. ¿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 errores o comportamientos anormales 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 = ... # API funciona
return resp.json()['url']
Pueden modificarlo fácilmente a:
import httpx
import uuid
from fastapi.responses import FileResponse
from fastapi import HTTPException
import os
@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
url = resp.json()['url'] # Asegurarse de que 'url' está definido, asumiendo que viene de 'resp'
response = client.get(url)
# Comprobar si la solicitud fue exitosa
if response.status_code == 200:
# Luego re-guardarlo
# Asegurarse de que el directorio './downloaded/' existe
os.makedirs("./downloaded", exist_ok=True)
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 Single Page Application (SPA) o un servidor Backend for Frontend (BFF) y prefieren no manejar las descargas y el almacenamiento de archivos directamente en su servidor, pueden usar un enfoque de proxy edge. 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 utilizando 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 ninguna 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 de pre-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 usar octet-stream por defecto
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 del 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' // Cachear durante 1 hora
}
});
return newResponse;
} catch (error) {
return new Response(`Error al obtener el archivo: ${error.message}`, {
status: 500,
headers: corsHeaders()
});
}
}
// Manejar las solicitudes de pre-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 edge 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 más fácilmente, sin dejar de cumplir con las políticas de CORS.
moving at the speed of creativity, achieving the depths of imagination.