Files
wekan/server/lib/syncWebhookRetention.js
Lauri Ojansivu 5a7b74bd1d Compact delivered Sync webhook plans, with their URLs and tokens, after 90 days.
Maintainer decision of 2026-09-30: receipts are compacted after 90 days to
a permanent minimal id and outcome. A stored webhook plan keeps each
target's URL, its X-Wekan-Token credential and the request body; a response
row keeps the receiver's reply and a comment plan the comment text - all
forever, and the plan had no completion evidence of its own.

The notification plan retention becomes a shared kit (syncPlanRetention.js)
and webhooks use it: once every target's delivery is confirmed, a completion
receipt with its time is written. After SYNC_RECEIPT_METADATA_DAYS (90) the
sweep first removes the plan's response and comment-plan rows - read only
while a delivery receipt is missing - then compacts the plan in place. A late
replay of the compact form returns as delivered; delivery and comment
receipts, already minimal, stay.

A MongoDB test covers what is removed and what an undelivered plan keeps,
replay of the compact form and its refusals; the full-app webhook test
compacts a really delivered plan, checks the URL and token are gone, and
replays it without touching the comment. 565 server tests pass.

Thanks to xet7 !
2026-09-30 11:39:05 +03:00

30 lines
1.6 KiB
JavaScript

'use strict';
// Retention for stored Sync webhook plans (maintainer decision of 2026-09-30),
// through the shared plan retention (syncPlanRetention.js). A webhook plan
// keeps each target's URL, its X-Wekan-Token credential and the request body;
// a response row keeps the receiver's reply, and a comment plan the comment
// text. After every target's delivery is confirmed a completion receipt is
// written, and SYNC_RECEIPT_METADATA_DAYS (90) later the response and comment
// plan rows - read only while a delivery receipt is missing - are removed and
// the plan is compacted. Delivery and comment receipts, already minimal, stay.
const kit = require('./syncPlanRetention').createPlanRetentionKit('sync-webhook');
// The rows only an uncompleted plan could need.
function webhookDependents({ responses, commentPlans }) {
return async row => {
const { deliveryId } = require('./syncWebhookPlan');
const ids = (row.plan?.targets || []).map(target => deliveryId(row.plan.activityId, target.integrationId));
if (!ids.length) return;
await responses.deleteMany({ _id: { $in: ids } });
await commentPlans.deleteMany({ _id: { $in: ids } });
};
}
function createSyncWebhookRetention({ plans, receipts, responses, commentPlans, ...options }) {
return kit.createPlanRetention({ plans, receipts, ...options,
beforeCompact: webhookDependents({ responses, commentPlans }) });
}
module.exports = { isCompactWebhookPlan: kit.isCompactPlan, recordWebhookCompletion: kit.recordPlanCompletion,
readCompactedWebhook: kit.readCompactedPlan, createSyncWebhookRetention, webhookDependents };