This commit is contained in:
taDuc
2026-04-20 23:27:38 +07:00
parent 7804ef842b
commit 6105a4c8da
4 changed files with 276 additions and 78 deletions

View File

@@ -2,6 +2,14 @@ const express = require("express");
const crypto = require("crypto");
const db = require("../db/polygons");
const { getBBox } = require("../utils/bbox");
const {
assertEditorSnapshotContract,
normalizeEditorSnapshotContract,
normalizeSectionCommitContract,
normalizeSectionContract,
normalizeSectionStateContract,
normalizeSectionSubmissionContract,
} = require("../types/contracts");
const router = express.Router();
const LOCK_TTL_MS = 15 * 60 * 1000;
@@ -673,6 +681,8 @@ function applyLinkScopeSnapshot(scope, now) {
}
function validateSnapshot(snapshot) {
assertEditorSnapshotContract(snapshot);
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) {
throw createHttpError("snapshot must be an object", 400);
}
@@ -847,71 +857,25 @@ function updateSubmissionReview(submissionId, status, actor, now, reviewNote) {
}
function normalizeSectionRow(row) {
return {
id: row.id,
title: row.title,
description: row.description,
user_id: row.user_id || null,
created_by: row.created_by,
created_at: row.created_at,
updated_at: row.updated_at,
state: {
status: row.status || "editing",
head_commit_id: row.head_commit_id || null,
version: Number(row.version || 0),
locked_by: row.locked_by || null,
locked_at: row.locked_at || null,
lock_expires_at: row.lock_expires_at || null,
},
};
return normalizeSectionContract(row);
}
function normalizeStateRow(row) {
return {
section_id: row.section_id,
status: row.status,
head_commit_id: row.head_commit_id || null,
version: Number(row.version || 0),
locked_by: row.locked_by || null,
locked_at: row.locked_at || null,
lock_expires_at: row.lock_expires_at || null,
updated_at: row.updated_at,
};
return normalizeSectionStateContract(row);
}
function normalizeCommitRow(row, includeSnapshot) {
const out = {
id: row.id,
section_id: row.section_id,
parent_commit_id: row.parent_commit_id || null,
commit_no: Number(row.commit_no),
kind: row.kind,
restored_from_commit_id: row.restored_from_commit_id || null,
created_by: row.created_by,
created_at: row.created_at,
title: row.title,
note: row.note,
snapshot_hash: row.snapshot_hash,
};
if (includeSnapshot) out.snapshot = parseSnapshotJson(row.snapshot_json);
return out;
if (includeSnapshot) {
return normalizeSectionCommitContract(row, parseSnapshotJson(row.snapshot_json));
}
return normalizeSectionCommitContract(row);
}
function normalizeSubmissionRow(row, includeSnapshot) {
const out = {
id: row.id,
section_id: row.section_id,
commit_id: row.commit_id,
submitted_by: row.submitted_by,
submitted_at: row.submitted_at,
status: row.status,
reviewed_by: row.reviewed_by || null,
reviewed_at: row.reviewed_at || null,
review_note: row.review_note || null,
snapshot_hash: row.snapshot_hash,
};
if (includeSnapshot) out.snapshot = parseSnapshotJson(row.snapshot_json);
return out;
if (includeSnapshot) {
return normalizeSectionSubmissionContract(row, parseSnapshotJson(row.snapshot_json));
}
return normalizeSectionSubmissionContract(row);
}
function buildEmptySnapshot(section) {
@@ -944,7 +908,7 @@ function normalizeSnapshotInput(input) {
function parseSnapshotJson(value) {
try {
return JSON.parse(value);
return normalizeEditorSnapshotContract(JSON.parse(value));
} catch (_err) {
return null;
}