Add BIM legacy share transform fallback

This commit is contained in:
CODEX 2026-07-02 20:49:18 +03:00
parent c242e364bb
commit 1a78dd0dc8
1 changed files with 109 additions and 4 deletions

View File

@ -1946,6 +1946,79 @@ const normalizeViewerSettings = (value) => (
: null
);
const viewerSettingsHaveTransforms = (settings) => {
const transforms = settings?.viewerState?.transforms;
return !!(
transforms &&
typeof transforms === "object" &&
!Array.isArray(transforms) &&
Object.keys(transforms).length
);
};
const modelRecordMatchesShare = (model, share) => {
const meta = model?.meta && typeof model.meta === "object" ? model.meta : {};
if (share?.assetId && meta.assetId === share.assetId) {
if (share.projectId && meta.projectId && meta.projectId !== share.projectId) {
return false;
}
if (share.versionId && meta.versionId && meta.versionId !== share.versionId) {
return false;
}
return true;
}
const haystack = [
model?.src,
meta.sourceSrc,
meta.settingsSrc,
meta.artifactSrc
].filter(Boolean).join("\n");
if (!haystack || !share?.assetId || !haystack.includes(share.assetId)) {
return false;
}
return !share.versionId || haystack.includes(share.versionId);
};
const readProjectViewerSettingsForShare = async (share) => {
if (!share?.assetId) return null;
let entries = [];
try {
entries = await fs.readdir(DATA_DIR, {withFileTypes: true});
} catch (_err) {
return null;
}
let best = null;
for (const entry of entries) {
if (!entry.isFile() || !entry.name.endsWith(".json") || entry.name === "index.json") {
continue;
}
const projectPath = path.join(DATA_DIR, entry.name);
const project = await readJSONFile(projectPath).catch(() => null);
if (!project || !Array.isArray(project.models)) {
continue;
}
if (!project.models.some((model) => modelRecordMatchesShare(model, share))) {
continue;
}
const settings = normalizeViewerSettings({
version: 1,
updatedAt: project.updatedAt || project.createdAt || nowIso(),
viewerState: project.viewerState || {},
design: project.design || null
});
if (!viewerSettingsHaveTransforms(settings)) {
continue;
}
const updatedAtMs = Date.parse(settings.updatedAt || "") || 0;
if (!best || updatedAtMs >= best.updatedAtMs) {
best = {settings, updatedAtMs};
}
}
return best?.settings || null;
};
const readViewerSettingsForModelSrc = async (src) => {
const manifestRef = await findManifestForModelSrc(src).catch(() => null);
if (!manifestRef || !existsSync(manifestRef.manifestPath)) {
@ -1955,6 +2028,33 @@ const readViewerSettingsForModelSrc = async (src) => {
return normalizeViewerSettings(manifest?.viewerSettings);
};
const mergeViewerSettingsTransforms = (base, fallback) => {
const normalizedBase = normalizeViewerSettings(base);
const normalizedFallback = normalizeViewerSettings(fallback);
if (!normalizedBase || viewerSettingsHaveTransforms(normalizedBase) || !viewerSettingsHaveTransforms(normalizedFallback)) {
return normalizedBase || normalizedFallback || null;
}
return {
...normalizedBase,
viewerState: {
...(normalizedBase.viewerState || {}),
transforms: normalizedFallback.viewerState.transforms,
models: normalizedFallback.viewerState.models || normalizedBase.viewerState?.models || []
}
};
};
const resolveShareViewerSettings = async (share) => {
const shareSettings = normalizeViewerSettings(share?.viewerSettings);
const manifestSettings = await readViewerSettingsForModelSrc(share?.settingsSrc || share?.src);
const base = shareSettings || manifestSettings;
if (viewerSettingsHaveTransforms(base)) {
return base;
}
const projectSettings = await readProjectViewerSettingsForShare(share);
return mergeViewerSettingsTransforms(base, projectSettings);
};
const handleCreateShare = async (req, res) => {
const session = await getValidatedBimSession(req, res);
if (BIM_AUTH_REQUIRED && !session) {
@ -1988,8 +2088,14 @@ const handleCreateShare = async (req, res) => {
const name = typeof payload?.name === "string" && payload.name.trim()
? payload.name.trim().slice(0, 240)
: path.basename(src);
const viewerSettings = normalizeViewerSettings(payload?.viewerSettings)
|| await readViewerSettingsForModelSrc(settingsSrc || src);
const viewerSettings = await resolveShareViewerSettings({
...identity,
src,
settingsSrc,
versionId: typeof payload?.versionId === "string" ? payload.versionId : null,
selectedLod: stringOrNull(payload?.selectedLod),
viewerSettings: normalizeViewerSettings(payload?.viewerSettings)
});
const share = {
token,
access: "public-readonly",
@ -2072,8 +2178,7 @@ const handleGetShare = async (req, res, token) => {
projectId: share.projectId,
assetId: share.assetId
}).catch(() => null);
const viewerSettings = normalizeViewerSettings(share.viewerSettings)
|| await readViewerSettingsForModelSrc(share.settingsSrc || share.src);
const viewerSettings = await resolveShareViewerSettings(share);
sendJSON(res, 200, {
ok: true,
mode: authenticated ? "standard" : "guest",