chore: import upstream snapshot with attribution
This commit is contained in:
+208
@@ -0,0 +1,208 @@
|
||||
import http from 'node:http'
|
||||
import { API_HOST, API_PORT, FAL_API_KEY, HUNYUAN_API_BASE, RODIN_API_KEY, TRIPO_API_KEY } from './server/config.mjs'
|
||||
import { assertLocalDiagnosticsRequest, readJsonBody, sendJson, setCorsHeaders } from './server/http-utils.mjs'
|
||||
import { createRequestId, logEvent, readRecentLogs, summarizeError, summarizePayload } from './server/logger.mjs'
|
||||
import { importLocalModel, proxyModel, serveLocalModel } from './server/model-store.mjs'
|
||||
import { createFalTask, getFalHealth, getFalTask } from './server/providers/fal.mjs'
|
||||
import { createHunyuanTask, getHunyuanHealth, getHunyuanTask } from './server/providers/hunyuan.mjs'
|
||||
import { createRodinTask, getRodinHealth, getRodinTask } from './server/providers/rodin.mjs'
|
||||
import { createTripoTask, getTripoHealth, getTripoTask } from './server/providers/tripo.mjs'
|
||||
import { analyzeAssetImage, getVisionHealth } from './server/providers/vision.mjs'
|
||||
|
||||
const DEFAULT_GENERATION_PROVIDER = 'rodin'
|
||||
|
||||
const server = http.createServer(async (request, response) => {
|
||||
const requestId = createRequestId()
|
||||
const startedAt = Date.now()
|
||||
let url = null
|
||||
|
||||
try {
|
||||
setCorsHeaders(response)
|
||||
response.setHeader('X-Request-Id', requestId)
|
||||
|
||||
if (request.method === 'OPTIONS') {
|
||||
response.writeHead(204)
|
||||
response.end()
|
||||
return
|
||||
}
|
||||
|
||||
url = new URL(request.url, `http://${request.headers.host}`)
|
||||
await logEvent('info', 'http.request', {
|
||||
requestId,
|
||||
method: request.method,
|
||||
path: url.pathname,
|
||||
query: Object.fromEntries(url.searchParams.entries()),
|
||||
})
|
||||
|
||||
if (request.method === 'GET' && url.pathname === '/api/3d/health') {
|
||||
const payload = {
|
||||
ok: true,
|
||||
providers: {
|
||||
tripo: getTripoHealth(),
|
||||
rodin: getRodinHealth(),
|
||||
hunyuan: getHunyuanHealth(),
|
||||
fal: getFalHealth(),
|
||||
vision: getVisionHealth(),
|
||||
},
|
||||
}
|
||||
sendJson(response, 200, payload)
|
||||
await logEvent('info', 'http.response', { requestId, path: url.pathname, status: 200, durationMs: Date.now() - startedAt })
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === 'GET' && url.pathname === '/api/3d/logs') {
|
||||
assertLocalDiagnosticsRequest(request)
|
||||
const payload = await readRecentLogs(url.searchParams.get('limit') || 100)
|
||||
sendJson(response, 200, payload)
|
||||
await logEvent('info', 'http.response', { requestId, path: url.pathname, status: 200, durationMs: Date.now() - startedAt, entries: payload.entries.length })
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === 'POST' && url.pathname === '/api/3d/analyze') {
|
||||
const payload = await readJsonBody(request)
|
||||
await logEvent('info', 'asset.analyze.start', {
|
||||
requestId,
|
||||
payload: summarizePayload(payload),
|
||||
})
|
||||
const insight = await analyzeAssetImage(payload)
|
||||
|
||||
sendJson(response, 200, insight)
|
||||
await logEvent('info', 'asset.analyze.success', {
|
||||
requestId,
|
||||
provider: insight.provider,
|
||||
configured: insight.configured,
|
||||
status: insight.status,
|
||||
categoryId: insight.categoryId,
|
||||
durationMs: Date.now() - startedAt,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === 'POST' && url.pathname === '/api/3d/generate') {
|
||||
const payload = await readJsonBody(request)
|
||||
const provider = payload.provider || DEFAULT_GENERATION_PROVIDER
|
||||
await logEvent('info', 'generation.create.start', {
|
||||
requestId,
|
||||
provider,
|
||||
payload: summarizePayload(payload),
|
||||
})
|
||||
const task = await createGenerationTask(provider, payload)
|
||||
|
||||
sendJson(response, 200, task)
|
||||
await logEvent('info', 'generation.create.success', {
|
||||
requestId,
|
||||
provider,
|
||||
taskId: task.taskId,
|
||||
status: task.status,
|
||||
durationMs: Date.now() - startedAt,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === 'GET' && url.pathname.startsWith('/api/3d/status/')) {
|
||||
const taskId = decodeURIComponent(url.pathname.replace('/api/3d/status/', ''))
|
||||
const provider = url.searchParams.get('provider') || DEFAULT_GENERATION_PROVIDER
|
||||
const task = await getGenerationTask(provider, taskId)
|
||||
|
||||
sendJson(response, 200, task)
|
||||
await logEvent('info', 'generation.status', {
|
||||
requestId,
|
||||
provider,
|
||||
taskId,
|
||||
status: task.status,
|
||||
progress: task.progress,
|
||||
hasModelUrl: Boolean(task.modelUrl),
|
||||
error: task.error,
|
||||
durationMs: Date.now() - startedAt,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === 'GET' && url.pathname === '/api/3d/model') {
|
||||
await proxyModel(url, response)
|
||||
await logEvent('info', 'model.proxy.success', { requestId, durationMs: Date.now() - startedAt })
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === 'POST' && url.pathname === '/api/3d/local-model') {
|
||||
const model = await importLocalModel(request, url)
|
||||
sendJson(response, 200, model)
|
||||
await logEvent('info', 'model.import.success', {
|
||||
requestId,
|
||||
taskId: model.taskId,
|
||||
modelUrl: model.modelUrl,
|
||||
fileName: model.fileName,
|
||||
durationMs: Date.now() - startedAt,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (request.method === 'GET' && url.pathname.startsWith('/api/3d/local-model/')) {
|
||||
await serveLocalModel(url, response)
|
||||
await logEvent('info', 'model.local.success', { requestId, path: url.pathname, durationMs: Date.now() - startedAt })
|
||||
return
|
||||
}
|
||||
|
||||
sendJson(response, 404, { error: 'Not found' })
|
||||
await logEvent('warn', 'http.not_found', { requestId, path: url.pathname, durationMs: Date.now() - startedAt })
|
||||
} catch (error) {
|
||||
if (response.headersSent) {
|
||||
await logEvent('error', 'http.stream_error', {
|
||||
requestId,
|
||||
path: url?.pathname,
|
||||
durationMs: Date.now() - startedAt,
|
||||
error: summarizeError(error),
|
||||
})
|
||||
response.destroy(error)
|
||||
return
|
||||
}
|
||||
|
||||
const status = error.status || 500
|
||||
sendJson(response, status, {
|
||||
error: error.message || 'Server error',
|
||||
detail: error.detail,
|
||||
})
|
||||
await logEvent(status >= 500 ? 'error' : 'warn', 'http.error', {
|
||||
requestId,
|
||||
method: request.method,
|
||||
path: url?.pathname,
|
||||
status,
|
||||
durationMs: Date.now() - startedAt,
|
||||
error: summarizeError(error),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
server.listen(API_PORT, API_HOST, () => {
|
||||
console.log(`Bio demo API running at http://${API_HOST}:${API_PORT}`)
|
||||
console.log(TRIPO_API_KEY ? 'Tripo API key loaded from environment.' : 'TRIPO_API_KEY is missing. Add it to .env.local.')
|
||||
console.log(RODIN_API_KEY ? 'Rodin API key loaded from environment.' : 'RODIN_API_KEY is missing. Add it to .env.local.')
|
||||
console.log(FAL_API_KEY ? 'Fal API key loaded from environment.' : 'FAL_API_KEY is missing. Add it to .env.local.')
|
||||
console.log(getVisionHealth().configured ? 'Vision analysis provider configured.' : 'Vision analysis is not configured. Add OPENAI_API_KEY to .env.local.')
|
||||
console.log(`Hunyuan3D local provider: ${HUNYUAN_API_BASE}`)
|
||||
logEvent('info', 'api.start', {
|
||||
host: API_HOST,
|
||||
port: API_PORT,
|
||||
providers: {
|
||||
tripo: Boolean(TRIPO_API_KEY),
|
||||
rodin: Boolean(RODIN_API_KEY),
|
||||
fal: Boolean(FAL_API_KEY),
|
||||
hunyuan: Boolean(HUNYUAN_API_BASE),
|
||||
vision: getVisionHealth().configured,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
function createGenerationTask(provider, payload) {
|
||||
if (provider === 'hunyuan') return createHunyuanTask(payload)
|
||||
if (provider === 'fal') return createFalTask(payload)
|
||||
if (provider === 'tripo') return createTripoTask(payload)
|
||||
return createRodinTask(payload)
|
||||
}
|
||||
|
||||
function getGenerationTask(provider, taskId) {
|
||||
if (provider === 'hunyuan') return getHunyuanTask(taskId)
|
||||
if (provider === 'fal') return getFalTask(taskId)
|
||||
if (provider === 'tripo') return getTripoTask(taskId)
|
||||
return getRodinTask(taskId)
|
||||
}
|
||||
Reference in New Issue
Block a user