This commit is contained in:
2026-07-19 10:50:09 +08:00
parent 4e142fd90d
commit 29dc0bbcce
+15 -1
View File
@@ -2029,7 +2029,7 @@ exports.bulkSyncQuestions = async (req, res) => {
const parent = await resolveQuestionParent(req.params); const parent = await resolveQuestionParent(req.params);
if (!parent?.parentRecord) return R.error(res, "Parent not found.", 404); if (!parent?.parentRecord) return R.error(res, "Parent not found.", 404);
const { questions = [], updatedBy } = req.body; const { questions = [], updatedBy, confirmFullReplace } = req.body;
const existing = await QuizQuestion.findAll({ const existing = await QuizQuestion.findAll({
where: { [parent.parentField]: parent.parentId, ...notDeleted }, where: { [parent.parentField]: parent.parentId, ...notDeleted },
@@ -2038,6 +2038,20 @@ exports.bulkSyncQuestions = async (req, res) => {
const incomingIds = questions.filter((q) => q.question_id).map((q) => q.question_id); const incomingIds = questions.filter((q) => q.question_id).map((q) => q.question_id);
const toArchive = existingIds.filter((id) => !incomingIds.includes(id)); const toArchive = existingIds.filter((id) => !incomingIds.includes(id));
// Safety guard: a sync where NONE of the currently-existing questions are
// referenced by id would archive the entire pool in one call — indistinguishable
// from a client sending a stale/incomplete array (exactly how real questions were
// nearly lost while diagnosing this endpoint). Partial edits (dropping a question
// or two out of many) are unaffected — only a full wipe requires explicit confirmation.
if (toArchive.length > 0 && toArchive.length === existingIds.length && !confirmFullReplace) {
await t.rollback();
return R.error(
res,
`This would archive all ${toArchive.length} existing question(s) and can't be recovered from here. Resend with confirmFullReplace: true if this is intentional.`,
409
);
}
if (toArchive.length) { if (toArchive.length) {
await QuizQuestion.update( await QuizQuestion.update(
{ deletedAt: new Date(), deletedBy: updatedBy ?? null }, { deletedAt: new Date(), deletedBy: updatedBy ?? null },