Estamos escrevendo para informar sobre uma próxima mudança que pode afetar como vocês acessam e usam os recursos de nossa plataforma.
Vamos aplicar a política CORS em todos os URLs de artefatos gerados. Isso significa que todos os recursos hospedados em nossa plataforma exigirão tratamento apropriado para requisições de origem cruzada, ou seja, vocês não poderão servir diretamente arquivos de modelos gerados usando o link que a API da Tripo retorna.
Esta mudança faz parte de nosso compromisso em melhorar a segurança e prevenir acesso não autorizado a recursos em diferentes origens. CORS (Cross-Origin Resource Sharing) é um mecanismo que permite que servidores especifiquem quais domínios externos podem acessar recursos.
Para mais informações sobre o que é CORS, vocês podem consultar o guia do MDN.
Atualmente, não fornecemos cabeçalhos CORS para URLs de artefatos. Como resultado, se vocês estiverem usando ou linkando diretamente para esses recursos em um navegador ou de domínios externos, poderão ter problemas.
Para garantir o acesso contínuo a esses ativos, vocês precisarão tomar uma das seguintes ações:
Precisam de exemplos de código práticos? Se estiverem inseguros sobre como começar ou acharem o processo difícil de implementar, fornecemos trechos de código práticos para ajudar vocês a iniciar. Por favor, consultem o Apêndice para mais detalhes.
Meu arquivo gerado foi perdido?
Não. Vocês sempre podem recuperar um novo URL da API a qualquer momento.
Eu não retorno seu URL diretamente, mas faço algum pós-processamento. Devo me preocupar?
Se vocês não estiverem retornando nosso URL diretamente para seus usuários, podem ignorar esta mudança. No entanto, recomendamos monitorar seu uso, especialmente se notarem quaisquer picos de erro ou comportamento anormal em seu sistema.
Se você estiver usando FastAPI (o que é fácil de portar ou traduzir para qualquer outro framework 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 works
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 = ... # same as above
# Add the code below
file_id = str(uuid.uuid4())
with httpx.Client() as client:
# Download the artifact
response = client.get(url)
# Check if the request was successful
if response.status_code == 200:
# Then resave it
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 o novo URL. Por exemplo, se seu aplicativo estiver hospedado em https://app.example.com, o URL que você deve servir será parecido com este:
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 de 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 esta 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) {
// Parse the request URL
const url = new URL(request.url);
// Get the target URL from the 'url' query parameter
const targetUrl = url.searchParams.get('url');
// If no URL is provided, return an error
if (!targetUrl) {
return new Response('Please provide a URL parameter', {
status: 400,
headers: {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
}
});
}
// Handle preflight OPTIONS request
if (request.method === 'OPTIONS') {
return handleCORS();
}
try {
// Fetch the file from the target URL
const response = await fetch(targetUrl);
// If the fetch failed, return the error
if (!response.ok) {
return new Response(`Failed to fetch from target URL: ${response.statusText}`, {
status: response.status,
headers: corsHeaders()
});
}
// Get the content type from the response or default to octet-stream
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
// Get the content disposition or create one from the URL
let contentDisposition = response.headers.get('Content-Disposition');
if (!contentDisposition) {
// Extract filename from the URL
const fileName = targetUrl.split('/').pop().split('?')[0] || 'file';
contentDisposition = `attachment; filename="${fileName}"`;
}
// Create a new response with CORS headers
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 for 1 hour
}
});
return newResponse;
} catch (error) {
return new Response(`Error fetching the file: ${error.message}`, {
status: 500,
headers: corsHeaders()
});
}
}
// Handle CORS preflight requests
function handleCORS() {
return new Response(null, {
status: 204, // No content
headers: corsHeaders()
});
}
// Create CORS headers object
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 hours
};
}
Uma vez que seu Cloudflare Worker esteja configurado, você pode integrá-lo em seu 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 => {
// Create a download link
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 solução é perfeita para SPAs ou servidores BFF que precisam contornar restrições CORS ao baixar e servir arquivos para usuários finais. O arquivo é buscado do 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 pelo Cloudflare: Cloudflare Workers - CORS Header Proxy
Isso permitirá que você lide com solicitações de origem cruzada mais facilmente, ainda em conformidade com as políticas CORS.
moving at the speed of creativity, achieving the depths of imagination.