openvibers/openvibe.coupons Commits

Public coupon events carry only a short reason code: a staff note or a calling service's free-text reason became the public lifecycle event's reason; anything that is not a code is 'other' (staff notes 'staff'), and report events drop a free-text reason

OP OpenVibers committed 24 Sept 2026 · d913e8759d · 2 files, +9 −2


Changes

ddiff --git a/server/domain/coupons.js b/server/domain/coupons.js
iindex 6adaafb..94a0795 100644
---- a/server/domain/coupons.js
++++ b/server/domain/coupons.js
@@@ -1,5 +1,12 @@
'use strict';
++/** A reason in a public event is a short code; a staff note or a calling service's free text never leaves Coupons. */
++function publicReason(reason) {
++ const r = String(reason || '');
++ if (r.startsWith('staff:')) return 'staff';
++ return /^[a-z][a-z0-9_]{0,39}$/.test(r) ? r : 'other';
++}
++
/**
* Codes: submission, restrictions, hints, evidence, status and confidence, expiry.
*
@@@ -265,7 +272,7 @@ function createCoupons({ store, merchants, publication }) {
if (statusChanged || confChanged) q.history.run(id, before.status, after.status, before.confidence, after.confidence, reason, actor, now);
if (statusChanged) {
const type = after.status === 'expired' ? 'coupons.coupon.expired' : after.status === 'disabled' ? 'coupons.coupon.disabled' : 'coupons.coupon.updated';
-- publication.emit(type, { type: 'coupon', id }, lifecyclePayload(after, { previous_status: before.status, reason: reason.startsWith('staff:') ? 'staff' : reason }), { isPublic: publiclyListed(after) || publiclyListed(before), traceparent });
++ publication.emit(type, { type: 'coupon', id }, lifecyclePayload(after, { previous_status: before.status, reason: publicReason(reason) }), { isPublic: publiclyListed(after) || publiclyListed(before), traceparent });
}
if (confChanged) {
publication.emit('coupons.confidence.changed', { type: 'coupon', id }, { merchant_id: after.merchant_id, from: before.confidence, to: after.confidence, status: after.status }, { traceparent });
ddiff --git a/server/domain/reports.js b/server/domain/reports.js
iindex fb49bd2..17f64db 100644
---- a/server/domain/reports.js
++++ b/server/domain/reports.js
@@@ -89,7 +89,7 @@ function createReports({ store, config, coupons, merchants, publication }) {
throw new ApiError(429, 'report.rate_limited', 'too many reports; try again later');
}
q.insert.run({ coupon_id: c.id, reporter_key: key, channel: who.channel, install_id: who.install, day, outcome, reason, now });
-- publication.emit('coupons.report.created', { type: 'coupon', id: c.id }, { merchant_id: c.merchant_id, outcome, ...(reason ? { reason } : {}), channel: who.channel, day }, { traceparent });
++ publication.emit('coupons.report.created', { type: 'coupon', id: c.id }, { merchant_id: c.merchant_id, outcome, ...(reason && /^[a-z][a-z0-9_]{0,39}$/.test(reason) ? { reason } : {}), channel: who.channel, day }, { traceparent });
} else {
q.correct.run({ id: existing.id, outcome, reason, channel: who.channel, install_id: who.install, now });
}