Align BIM comments with OPS detail card

This commit is contained in:
CODEX
2026-06-20 17:21:07 +03:00
parent 684f85b382
commit d70f6e0886
4 changed files with 800 additions and 192 deletions
+39 -6
View File
@@ -1578,22 +1578,47 @@ const sanitizeCommentAttachments = (value) => {
return [];
}
const dataUrl = typeof item.dataUrl === "string" ? item.dataUrl : "";
if (!/^data:image\/(png|jpe?g|webp|gif);base64,/i.test(dataUrl)) {
if (!/^data:[^;,]+\/?[^;,]*;base64,/i.test(dataUrl)) {
return [];
}
if (dataUrl.length > 4_500_000) {
const size = Number(item.size);
if (Number.isFinite(size) && size > 50 * 1024 * 1024) {
return [];
}
if (dataUrl.length > 70_000_000) {
return [];
}
return [{
id: stringOrNull(item.id) || `att_${randomBase64Url(8)}`,
name: sanitizeFilename(item.name || "image", "image"),
type: stringOrNull(item.type) || "image",
size: Number.isFinite(Number(item.size)) ? Number(item.size) : null,
name: sanitizeFilename(item.name || "file", "file"),
type: stringOrNull(item.type) || "application/octet-stream",
size: Number.isFinite(size) ? size : null,
dataUrl
}];
});
};
const sanitizeCommentSubitems = (value) => {
if (!Array.isArray(value)) {
return [];
}
return value.slice(0, 100).flatMap((item) => {
if (!item || typeof item !== "object") {
return [];
}
const title = sanitizeCommentText(item.title ?? item.text ?? item.name, 300);
if (!title) {
return [];
}
return [{
id: stringOrNull(item.id) || `sub_${randomBase64Url(8)}`,
title,
done: item.done === true,
createdAt: stringOrNull(item.createdAt) || nowIso()
}];
});
};
const resolveCommentSource = (payload = {}) => {
const identity = resolveAssetIdentityFromPayload(payload);
const rawSrc = stringOrNull(payload.src) ||
@@ -1649,6 +1674,7 @@ const publicComment = (comment) => ({
objectId: comment.objectId || null,
title: comment.title || "",
body: comment.body || "",
subitems: Array.isArray(comment.subitems) ? comment.subitems : [],
worldPos: comment.worldPos || null,
createdAt: comment.createdAt,
updatedAt: comment.updatedAt,
@@ -1722,6 +1748,7 @@ const handleCreateComment = async (req, res) => {
objectId: stringOrNull(payload?.objectId),
title: title || "Комментарий",
body,
subitems: sanitizeCommentSubitems(payload?.subitems),
worldPos,
createdAt: now,
updatedAt: now,
@@ -1773,11 +1800,14 @@ const handlePatchComment = async (req, res, commentId) => {
}
const hasTitle = Object.prototype.hasOwnProperty.call(payload, "title");
const hasBody = Object.prototype.hasOwnProperty.call(payload, "body");
const hasSubitems = Object.prototype.hasOwnProperty.call(payload, "subitems");
const nextTitle = hasTitle ? sanitizeCommentText(payload.title, 180) || comment.title || "Комментарий" : comment.title;
const nextBody = hasBody ? sanitizeCommentText(payload.body, 8000) : comment.body;
const nextSubitems = hasSubitems ? sanitizeCommentSubitems(payload.subitems) : (Array.isArray(comment.subitems) ? comment.subitems : []);
const titleChanged = hasTitle && nextTitle !== comment.title;
const bodyChanged = hasBody && nextBody !== comment.body;
if ((titleChanged || bodyChanged) && !isOwner) {
const subitemsChanged = hasSubitems && JSON.stringify(nextSubitems) !== JSON.stringify(comment.subitems || []);
if ((titleChanged || bodyChanged || subitemsChanged) && !isOwner) {
sendJSON(res, 403, {ok: false, error: "comment_owner_required"});
return;
}
@@ -1787,6 +1817,9 @@ const handlePatchComment = async (req, res, commentId) => {
if (bodyChanged) {
comment.body = nextBody;
}
if (subitemsChanged) {
comment.subitems = nextSubitems;
}
const nextMessage = buildCommentMessage(payload?.message || null, user);
if (nextMessage) {
comment.messages = Array.isArray(comment.messages) ? comment.messages : [];