저희 플랫폼의 리소스에 액세스하고 사용하는 방식에 영향을 미칠 수 있는 예정된 변경 사항에 대해 알려드립니다.
2025년 4월 9일부터 생성된 모든 아티팩트 URL에 CORS 정책을 적용할 예정입니다. 이는 저희 플랫폼에서 호스팅되는 모든 리소스가 교차 출처 요청에 대한 적절한 처리를 요구한다는 것을 의미합니다. 즉, Tripo API가 반환하는 링크를 사용하여 생성된 모델 파일을 직접 제공할 수 없습니다.
이 변경은 보안을 강화하고 다른 출처의 리소스에 대한 무단 액세스를 방지하려는 저희의 노력의 일환입니다. CORS(Cross-Origin Resource Sharing)는 서버가 어떤 외부 도메인이 리소스에 액세스할 수 있는지 지정할 수 있도록 하는 메커니즘입니다.
CORS에 대한 자세한 정보는 MDN의 가이드를 참조하십시오.
현재 저희는 아티팩트 URL에 대한 CORS 헤더를 제공하지 않습니다. 따라서 브라우저에서 또는 외부 도메인에서 이러한 리소스를 직접 사용하거나 링크하는 경우 문제가 발생할 수 있습니다.
이러한 자산에 계속 액세스하려면 다음 조치 중 하나를 취해야 합니다.
적용은 2025년 4월 9일부터 시작됩니다.
실용적인 코드 예제가 필요하십니까? 시작하는 방법을 모르거나 구현하기 어렵다고 생각되면, 시작하는 데 도움이 되는 실용적인 코드 스니펫을 제공했습니다. 자세한 내용은 부록을 참조하십시오.
생성된 파일이 손실됩니까?
아니요. 언제든지 API에서 새로운 URL을 검색할 수 있습니다.
귀하의 URL을 직접 반환하지는 않지만 일부 후처리 작업을 수행합니다. 걱정해야 합니까?
사용자에게 저희 URL을 직접 반환하지 않는 경우 이 변경 사항을 건너뛸 수 있습니다. 그러나 특히 시스템에서 오류 급증이나 비정상적인 동작을 발견하는 경우 사용량을 모니터링하는 것이 좋습니다.
FastAPI (다른 프레임워크나 언어로 쉽게 포팅하거나 번역할 수 있음)를 사용하는 경우, CORS를 처리하고 아티팩트를 로컬에 저장하도록 기존 코드를 조정하는 방법은 다음과 같습니다.
현재 코드:
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # API works
return resp.json()['url']
다음과 같이 쉽게 수정할 수 있습니다.
import httpx
import uuid
import os
from fastapi.responses import FileResponse
from fastapi import HTTPException
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # same as above
url = resp.json()['url'] # Assuming 'url' is extracted from resp.json()
# Add the code below
file_id = str(uuid.uuid4())
# Ensure the directory exists
os.makedirs("./downloaded", exist_ok=True)
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)
else:
raise HTTPException(status_code=response.status_code, detail="Failed to download artifact")
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")
파일을 로컬에 다운로드하고 저장한 후에는 새 URL로 사용자에게 서비스를 제공해야 합니다. 예를 들어, 애플리케이션이 https://app.example.com에 호스팅되어 있는 경우, 제공해야 하는 URL은 다음과 같습니다.
https://app.example.com/artifact/<file_id>
이 코드 스니펫은 통합을 조정하는 방법을 보여주는 예시일 뿐입니다. 그러나 몇 가지 중요한 고려 사항이 있습니다.
단일 페이지 애플리케이션(SPA) 또는 프런트엔드를 위한 백엔드(BFF) 서버를 개발 중이며 서버에서 직접 파일 다운로드 및 저장을 처리하고 싶지 않다면, 엣지 프록시 방식을 사용할 수 있습니다. 이를 통해 저희 서버에서 파일을 가져와 필요한 CORS 헤더를 적용한 다음 사용자에게 제공할 수 있습니다.
다음은 Cloudflare Workers를 사용하여 이 방식을 구현하는 방법의 예입니다. 이 솔루션은 파일을 다운로드하여 도메인 아래에서 재전송함으로써 CORS 문제를 처리합니다.
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
};
}
Cloudflare Worker가 설정되면 다음 JavaScript 코드를 사용하여 파일을 가져와 SPA에 통합할 수 있습니다.
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));
이 솔루션은 최종 사용자에게 파일을 다운로드하고 제공하면서 CORS 제한을 우회해야 하는 SPA 또는 BFF 서버에 적합합니다. 파일은 대상 URL에서 가져와 엣지 프록시를 통해 전달된 다음 필요한 CORS 헤더와 함께 제공됩니다.
간단한 CORS 프록시를 설정하려면 Cloudflare에서 제공하는 다음 예시를 참조하십시오: Cloudflare Workers - CORS Header Proxy
이를 통해 CORS 정책을 준수하면서 교차 출처 요청을 더 쉽게 처리할 수 있습니다.
moving at the speed of creativity, achieving the depths of imagination.