beam: add per-model viewer settings

This commit is contained in:
CODEX
2026-06-04 22:36:15 +03:00
parent 09b1173697
commit a95a7d5c38
6 changed files with 1070 additions and 81 deletions
+152
View File
@@ -230,6 +230,77 @@ const resolveUploadSrc = (src) => {
return resolved;
};
const normalizeUploadSrcValue = (src) => {
const resolved = resolveUploadSrc(src);
return resolved ? srcFromUploadPath(resolved) : null;
};
const findManifestForModelSrc = async (src) => {
const uploadPath = resolveUploadSrc(src);
if (!uploadPath) {
return null;
}
const sourceSrc = srcFromUploadPath(uploadPath);
const sourceFormat = CONVERTIBLE_MODEL_FORMATS.get(path.extname(uploadPath).toLowerCase());
if (sourceFormat) {
return {
manifestPath: manifestPathForSource(uploadPath),
modelPath: uploadPath,
sourceSrc
};
}
const directManifestPath = manifestPathForSource(uploadPath);
if (existsSync(directManifestPath)) {
return {
manifestPath: directManifestPath,
modelPath: uploadPath,
sourceSrc
};
}
let entries = [];
try {
entries = await fs.readdir(path.dirname(uploadPath));
} catch (_) {
return {
manifestPath: directManifestPath,
modelPath: uploadPath,
sourceSrc
};
}
for (const entry of entries) {
if (!entry.endsWith(".beam.json")) {
continue;
}
const candidatePath = path.join(path.dirname(uploadPath), entry);
const manifest = await readJSONFile(candidatePath);
if (!manifest || typeof manifest !== "object") {
continue;
}
const relatedSrcs = [
manifest.sourceSrc,
manifest.artifactSrc,
manifest.fallbackArtifactSrc,
manifest.metadataSrc
].map(normalizeUploadSrcValue).filter(Boolean);
if (relatedSrcs.includes(sourceSrc)) {
return {
manifestPath: candidatePath,
modelPath: uploadPath,
sourceSrc: normalizeUploadSrcValue(manifest.sourceSrc) || sourceSrc
};
}
}
return {
manifestPath: directManifestPath,
modelPath: uploadPath,
sourceSrc
};
};
const readJSONFile = async (filePath) => {
try {
const raw = await fs.readFile(filePath, "utf8");
@@ -347,6 +418,79 @@ const handleConversionStatus = async (res, searchParams) => {
sendJSON(res, 200, manifest);
};
const handleGetModelSettings = async (res, searchParams) => {
const manifestRef = await findManifestForModelSrc(searchParams.get("src"));
if (!manifestRef) {
sendText(res, 400, "Invalid model path");
return;
}
if (!existsSync(manifestRef.modelPath)) {
sendText(res, 404, "Model file not found");
return;
}
const manifest = await readJSONFile(manifestRef.manifestPath);
sendJSON(res, 200, {
sourceSrc: manifest?.sourceSrc || manifestRef.sourceSrc,
artifactSrc: manifest?.artifactSrc || null,
fallbackArtifactSrc: manifest?.fallbackArtifactSrc || null,
viewerSettings: manifest?.viewerSettings || null
});
};
const handlePutModelSettings = async (req, res, searchParams) => {
const manifestRef = await findManifestForModelSrc(searchParams.get("src"));
if (!manifestRef) {
sendText(res, 400, "Invalid model path");
return;
}
if (!existsSync(manifestRef.modelPath)) {
sendText(res, 404, "Model file not found");
return;
}
let payload = {};
try {
payload = await parseJSONBody(req);
} catch (err) {
sendText(res, 400, err.message || "Invalid JSON");
return;
}
const viewerSettings = payload?.viewerSettings;
if (!viewerSettings || typeof viewerSettings !== "object" || Array.isArray(viewerSettings)) {
sendText(res, 400, "viewerSettings object is required");
return;
}
const existing = await readJSONFile(manifestRef.manifestPath) || {};
const now = nowIso();
const nextManifest = {
...existing,
sourceSrc: existing.sourceSrc || manifestRef.sourceSrc,
status: existing.status || "ready",
viewerSettings: {
...viewerSettings,
updatedAt: viewerSettings.updatedAt || now
},
viewerSettingsUpdatedAt: now,
updatedAt: existing.updatedAt || now
};
try {
await writeJSONFile(manifestRef.manifestPath, nextManifest);
sendJSON(res, 200, {
sourceSrc: nextManifest.sourceSrc,
artifactSrc: nextManifest.artifactSrc || null,
fallbackArtifactSrc: nextManifest.fallbackArtifactSrc || null,
viewerSettings: nextManifest.viewerSettings
});
} catch (err) {
console.error("[server] failed to save model settings", err);
sendText(res, 500, "Failed to save model settings");
}
};
const buildProjectPayload = (body) => {
const now = new Date().toISOString();
const id = `proj_${crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2)}`;
@@ -634,6 +778,14 @@ const requestHandler = async (req, res) => {
return handleConversionStatus(res, url.searchParams);
}
if (req.method === "GET" && url.pathname === "/api/model-settings") {
return handleGetModelSettings(res, url.searchParams);
}
if (req.method === "PUT" && url.pathname === "/api/model-settings") {
return handlePutModelSettings(req, res, url.searchParams);
}
await serveStatic(req, res, url);
};