Escrevemos para informar sobre uma próxima mudança que pode afetar como vocês acessam e usam recursos da nossa plataforma.
A partir de 09/04/2025, estaremos aplicando a política CORS em todas as URLs de artefatos gerados. Isso significa que todos os recursos hospedados em nossa plataforma exigirão tratamento adequado para requisições cross-origin, ou seja, você não poderá servir diretamente arquivos de modelo gerados usando o link que a API da Tripo retorna.
Esta mudança faz parte do nosso compromisso em melhorar a segurança e prevenir o acesso não autorizado a recursos em diferentes origens. CORS (Cross-Origin Resource Sharing) é um mecanismo que permite aos servidores especificar quais domínios externos podem acessar recursos.
Para mais informações sobre o que é CORS, você pode consultar o guia da MDN.
Atualmente, não fornecemos cabeçalhos CORS para URLs de artefatos. Como resultado, se você estiver usando ou linkando para esses recursos diretamente em um navegador ou de domínios externos, poderá ter problemas.
Para garantir o acesso contínuo a esses ativos, você precisará realizar uma das seguintes ações:
A aplicação começa em 09/04/2025.
Precisa de exemplos de código práticos? Se você não tem certeza de como começar ou acha o processo difícil de implementar, fornecemos trechos de código práticos para ajudar você a iniciar. Consulte o Apêndice para mais detalhes.
Meu arquivo gerado foi perdido?
Não. Você sempre pode recuperar uma nova URL da API a qualquer momento.
Não retorno sua URL diretamente, mas faço algum pós-processamento. Devo me preocupar?
Se você não estiver retornando nossa URL diretamente para seus usuários, pode ignorar esta mudança. No entanto, recomendamos monitorar seu uso, especialmente se notar quaisquer picos de erro ou comportamento anormal em seu sistema.
Se você estiver usando FastAPI (que é fácil de portar ou traduzir para qualquer outra estrutura ou linguagem), veja como você pode ajustar seu código existente para lidar com CORS e salvar o artefato localmente:
Código Atual:
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # API funciona
return resp.json()['url']
Você pode facilmente modificá-lo para:
import httpx
import uuid
from fastapi.responses import FileResponse
from fastapi import HTTPException
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # igual ao acima
# Adicione o código abaixo
file_id = str(uuid.uuid4())
with httpx.Client() as client:
# Baixe o artefato
response = client.get(url)
# Verifique se a requisição foi bem-sucedida
if response.status_code == 200:
# Então salve-o novamente
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")
Após baixar e salvar o arquivo localmente, você deve servir aos seus usuários com a nova URL. Por exemplo, se seu aplicativo estiver hospedado em https://app.example.com, a URL que você deve servir será assim:
https://app.example.com/artifact/<file_id>
Este trecho de código é apenas uma demonstração de como ajustar sua integração. No entanto, há algumas coisas importantes que você deve considerar:
Se você está desenvolvendo um Single Page Application (SPA) ou um servidor Backend for Frontend (BFF) e prefere não lidar com downloads e armazenamento de arquivos diretamente em seu servidor, você pode usar uma abordagem de proxy de borda. Isso permite que você busque o arquivo do nosso servidor, aplique os cabeçalhos CORS necessários e, em seguida, o sirva aos seus usuários.
Abaixo está um exemplo de como implementar essa abordagem usando Cloudflare Workers. Esta solução tratará os problemas de CORS baixando o arquivo e reenviando-o sob seu domínio.
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
// Analise a URL da requisição
const url = new URL(request.url);
// Obtenha a URL de destino do parâmetro de consulta 'url'
const targetUrl = url.searchParams.get('url');
// Se nenhuma URL for fornecida, retorne um erro
if (!targetUrl) {
return new Response('Por favor, forneça um 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'
}
});
}
// Lidar com a requisição OPTIONS de preflight
if (request.method === 'OPTIONS') {
return handleCORS();
}
try {
// Busque o arquivo da URL de destino
const response = await fetch(targetUrl);
// Se a busca falhou, retorne o erro
if (!response.ok) {
return new Response(`Falha ao buscar da URL de destino: ${response.statusText}`, {
status: response.status,
headers: corsHeaders()
});
}
// Obtenha o tipo de conteúdo da resposta ou use octet-stream por padrão
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
// Obtenha a disposição do conteúdo ou crie uma a partir da URL
let contentDisposition = response.headers.get('Content-Disposition');
if (!contentDisposition) {
// Extraia o nome do arquivo da URL
const fileName = targetUrl.split('/').pop().split('?')[0] || 'file';
contentDisposition = `attachment; filename="${fileName}"`;
}
// Crie uma nova resposta com cabeçalhos 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' // Cache por 1 hora
}
});
return newResponse;
} catch (error) {
return new Response(`Erro ao buscar o arquivo: ${error.message}`, {
status: 500,
headers: corsHeaders()
});
}
}
// Lidar com requisições CORS preflight
function handleCORS() {
return new Response(null, {
status: 204, // Sem conteúdo
headers: corsHeaders()
});
}
// Criar objeto de cabeçalhos 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
};
}
Uma vez que seu Cloudflare Worker esteja configurado, você pode integrá-lo em sua SPA usando o seguinte código JavaScript para buscar o arquivo:
fetch('https://your-worker-url.workers.dev/?url=<target_url>')
.then(response => response.blob())
.then(blob => {
// Crie um link de download
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('Erro:', error));
Esta solução é perfeita para SPAs ou servidores BFF que precisam contornar restrições de CORS ao baixar e servir arquivos para usuários finais. O arquivo é buscado da URL de destino, passado pelo proxy de borda e servido com os cabeçalhos CORS necessários.
Se você preferir configurar um proxy CORS simples, pode consultar este exemplo fornecido pela Cloudflare: Cloudflare Workers - CORS Header Proxy
Isso permitirá que você lide com requisições cross-origin mais facilmente, enquanto ainda cumpre as políticas de CORS.
moving at the speed of creativity, achieving the depths of imagination.