fix: harden upload resilience and contracts
This commit is contained in:
+61
-16
@@ -7,6 +7,7 @@ import { compressTextureBuffer } from '@/lib/texture-compression'
|
||||
import { classifyAssetCategory } from '@/lib/asset-classification'
|
||||
import { normalizeTextureFilename } from '@/lib/asset-naming'
|
||||
import { TEXTURE_EXTENSIONS, TMP_DIR } from '@/lib/constants'
|
||||
import { getErrorMessage, isRecord } from '@/lib/guards'
|
||||
import { getModelAssetPath } from '@/lib/model-paths'
|
||||
import type { GitModelMode, ParsedFile, PreparedAssetSummary, PreparedGitAssetsResult, PushFile } from '@/lib/types'
|
||||
|
||||
@@ -18,6 +19,30 @@ interface PrepareGitAssetsParams {
|
||||
|
||||
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
||||
|
||||
function isJsonValue(value: unknown): value is JsonValue {
|
||||
if (value === null) return true
|
||||
|
||||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.every(isJsonValue)
|
||||
}
|
||||
|
||||
return isRecord(value) && Object.values(value).every(isJsonValue)
|
||||
}
|
||||
|
||||
function parseJsonValue(content: string) {
|
||||
const parsed: unknown = JSON.parse(content)
|
||||
|
||||
if (!isJsonValue(parsed)) {
|
||||
throw new Error('model.gltf contient un JSON invalide')
|
||||
}
|
||||
|
||||
return parsed
|
||||
}
|
||||
|
||||
function getTextureFilenameMap(parsedFiles: ParsedFile[]) {
|
||||
const filenameMap = new Map<string, string>()
|
||||
const normalizedGroups = new Map<string, Array<{ original: string; normalized: string }>>()
|
||||
@@ -75,7 +100,7 @@ function rewriteGltfUris(value: JsonValue, filenameMap: Map<string, string>): Js
|
||||
function prepareModelBuffer(buffer: Buffer, filenameMap: Map<string, string>) {
|
||||
if (filenameMap.size === 0) return buffer
|
||||
|
||||
const parsed = JSON.parse(buffer.toString('utf-8')) as JsonValue
|
||||
const parsed = parseJsonValue(buffer.toString('utf-8'))
|
||||
return Buffer.from(JSON.stringify(rewriteGltfUris(parsed, filenameMap), null, 2), 'utf-8')
|
||||
}
|
||||
|
||||
@@ -130,7 +155,14 @@ async function prepareSeparateFiles(
|
||||
})
|
||||
}
|
||||
|
||||
return { filesToPush, modelFilename, assetSummaries, compressed, compressionError }
|
||||
return {
|
||||
filesToPush,
|
||||
modelFilename,
|
||||
assetSummaries,
|
||||
compressed,
|
||||
compressionError,
|
||||
deliveryMode: 'keep-gltf' as const,
|
||||
}
|
||||
}
|
||||
|
||||
async function prepareDracoGlb(
|
||||
@@ -160,22 +192,35 @@ async function prepareDracoGlb(
|
||||
await writeFile(join(tmpFolder, filename), content)
|
||||
}
|
||||
|
||||
const result = await compressWithBlender(inputModelPath, outputModelPath)
|
||||
if (!result.success || !existsSync(outputModelPath)) {
|
||||
throw new Error(result.error || 'Compression Blender echouee')
|
||||
}
|
||||
try {
|
||||
const result = await compressWithBlender(inputModelPath, outputModelPath)
|
||||
if (!result.success || !existsSync(outputModelPath)) {
|
||||
throw new Error(result.error || 'Compression Blender echouee')
|
||||
}
|
||||
|
||||
const content = await readFile(outputModelPath)
|
||||
const modelFilename = 'model.glb'
|
||||
const content = await readFile(outputModelPath)
|
||||
const modelFilename = 'model.glb'
|
||||
|
||||
return {
|
||||
filesToPush: [{
|
||||
path: getModelAssetPath(folderName, modelFilename),
|
||||
contentBase64: content.toString('base64'),
|
||||
}],
|
||||
modelFilename,
|
||||
assetSummaries: [{ filename: modelFilename, kind: 'model', compressed: true }],
|
||||
compressed: true,
|
||||
return {
|
||||
filesToPush: [{
|
||||
path: getModelAssetPath(folderName, modelFilename),
|
||||
contentBase64: content.toString('base64'),
|
||||
}],
|
||||
modelFilename,
|
||||
assetSummaries: [{ filename: modelFilename, kind: 'model', compressed: true }],
|
||||
compressed: true,
|
||||
deliveryMode: 'draco-glb',
|
||||
}
|
||||
} catch (err) {
|
||||
const fallback = await prepareSeparateFiles(folderName, parsedFiles, textureFilenameMap)
|
||||
const message = getErrorMessage(err, 'Compression Blender echouee')
|
||||
|
||||
return {
|
||||
...fallback,
|
||||
compressionError: fallback.compressionError
|
||||
? `${message}. ${fallback.compressionError}`
|
||||
: message,
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await rm(tmpFolder, { recursive: true, force: true }).catch(() => {})
|
||||
|
||||
Reference in New Issue
Block a user