nodebb-plugin-calendar-onekite 11.1.52 → 11.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.js +6 -0
- package/lib/helloasso.js +19 -15
- package/lib/helloassoWebhook.js +17 -4
- package/package.json +1 -1
- package/public/client.js +2 -2
- package/templates/admin/plugins/calendar-onekite.tpl +6 -0
package/lib/api.js
CHANGED
|
@@ -203,6 +203,12 @@ api.getEvents = async function (req, res) {
|
|
|
203
203
|
ev.extendedProps.canModerate = canMod;
|
|
204
204
|
ev.extendedProps.total = r.total || 0;
|
|
205
205
|
ev.extendedProps.createdAt = r.createdAt || null;
|
|
206
|
+
// Expose payment URL only to the requester (or moderators) when awaiting payment
|
|
207
|
+
if (r.status === 'awaiting_payment' && r.paymentUrl && (/^https?:\/\//i).test(String(r.paymentUrl))) {
|
|
208
|
+
if ((req.uid && String(req.uid) === String(r.uid)) || canMod) {
|
|
209
|
+
ev.extendedProps.paymentUrl = String(r.paymentUrl);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
206
212
|
out.push(ev);
|
|
207
213
|
}
|
|
208
214
|
}
|
package/lib/helloasso.js
CHANGED
|
@@ -14,31 +14,35 @@ function requestJson(method, url, headers = {}, bodyObj = null) {
|
|
|
14
14
|
port: u.port || 443,
|
|
15
15
|
path: u.pathname + u.search,
|
|
16
16
|
headers: {
|
|
17
|
-
|
|
17
|
+
Accept: 'application/json',
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
...(body ? { 'Content-Length': Buffer.byteLength(body) } : {}),
|
|
18
20
|
...headers,
|
|
19
|
-
...(body ? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } : {}),
|
|
20
21
|
},
|
|
21
22
|
},
|
|
22
|
-
(
|
|
23
|
+
(resp) => {
|
|
23
24
|
let data = '';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const status =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
25
|
+
resp.setEncoding('utf8');
|
|
26
|
+
resp.on('data', (chunk) => { data += chunk; });
|
|
27
|
+
resp.on('end', () => {
|
|
28
|
+
const status = resp.statusCode || 0;
|
|
29
|
+
|
|
30
|
+
// Try JSON first, but never throw from here (avoid crashing NodeBB).
|
|
31
31
|
try {
|
|
32
|
-
const json = JSON.parse(data);
|
|
33
|
-
resolve({ status, json });
|
|
32
|
+
const json = data ? JSON.parse(data) : null;
|
|
33
|
+
return resolve({ status, json });
|
|
34
34
|
} catch (e) {
|
|
35
|
-
|
|
35
|
+
// Non-JSON response (HTML/proxy error/etc.)
|
|
36
|
+
const snippet = String(data || '').slice(0, 500);
|
|
37
|
+
return resolve({ status, json: null, raw: snippet });
|
|
36
38
|
}
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
);
|
|
40
42
|
|
|
41
|
-
req.on('error',
|
|
43
|
+
req.on('error', (err) => {
|
|
44
|
+
resolve({ status: 0, json: null, error: err && err.message ? err.message : String(err) });
|
|
45
|
+
});
|
|
42
46
|
if (body) req.write(body);
|
|
43
47
|
req.end();
|
|
44
48
|
});
|
|
@@ -246,4 +250,4 @@ module.exports = {
|
|
|
246
250
|
extractCatalogItems,
|
|
247
251
|
listCatalogItems,
|
|
248
252
|
createCheckoutIntent,
|
|
249
|
-
};
|
|
253
|
+
};
|
package/lib/helloassoWebhook.js
CHANGED
|
@@ -145,7 +145,7 @@ function buildBodyCandidates(req) {
|
|
|
145
145
|
|
|
146
146
|
async function verifySignature(req) {
|
|
147
147
|
const settings = await meta.settings.get(SETTINGS_KEY);
|
|
148
|
-
const secret = settings && settings.helloassoClientSecret ? String(settings.helloassoClientSecret) : '';
|
|
148
|
+
const secret = settings && (settings.helloassoSignatureKey || settings.helloassoClientSecret) ? String(settings.helloassoSignatureKey || settings.helloassoClientSecret) : '';
|
|
149
149
|
const provided = req.headers['x-ha-signature'] || req.headers['X-HA-Signature'];
|
|
150
150
|
if (!secret || !provided) return false;
|
|
151
151
|
|
|
@@ -254,13 +254,26 @@ function getReservationIdFromPayload(payload) {
|
|
|
254
254
|
*/
|
|
255
255
|
async function handler(req, res, next) {
|
|
256
256
|
try {
|
|
257
|
+
if (req.method === 'GET') {
|
|
258
|
+
return res.json({ ok: true });
|
|
259
|
+
}
|
|
257
260
|
if (req.method !== 'POST') {
|
|
258
261
|
return res.status(405).json({ ok: false, error: 'method-not-allowed' });
|
|
259
262
|
}
|
|
260
263
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
// Verify signature if a signatureKey is configured; otherwise accept but log a warning.
|
|
265
|
+
const settings = await meta.settings.get(SETTINGS_KEY);
|
|
266
|
+
const hasSigKey = settings && settings.helloassoSignatureKey;
|
|
267
|
+
if (hasSigKey) {
|
|
268
|
+
const sigOk = await verifySignature(req);
|
|
269
|
+
if (!sigOk) {
|
|
270
|
+
// eslint-disable-next-line no-console
|
|
271
|
+
console.warn('[calendar-onekite] HelloAsso webhook invalid signature', { ip: req.ip, ua: req.headers['user-agent'] });
|
|
272
|
+
return res.status(401).json({ ok: false, error: 'invalid-signature' });
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
// eslint-disable-next-line no-console
|
|
276
|
+
console.warn('[calendar-onekite] HelloAsso webhook signatureKey not set; skipping signature verification');
|
|
264
277
|
}
|
|
265
278
|
|
|
266
279
|
// At this point, the payload is trusted.
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -287,7 +287,7 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
287
287
|
const rid = p.rid || ev.id;
|
|
288
288
|
const status = p.status || '';
|
|
289
289
|
const itemsHtml = (() => {
|
|
290
|
-
const names = Array.isArray(p.itemNames) ? p.itemNames : (p.itemName ? [p.itemName] : []);
|
|
290
|
+
const names = Array.isArray(p.itemNames) ? p.itemNames : (typeof p.itemNames === 'string' && p.itemNames.trim() ? p.itemNames.split(',').map(s=>s.trim()).filter(Boolean) : (p.itemName ? [p.itemName] : []));
|
|
291
291
|
if (names.length) {
|
|
292
292
|
return `<ul style="margin:0 0 0 1.1rem; padding:0;">${names.map(n => `<li>${String(n).replace(/</g,'<').replace(/>/g,'>')}</li>`).join('')}</ul>`;
|
|
293
293
|
}
|
|
@@ -361,7 +361,7 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
|
|
|
361
361
|
callback: async () => {
|
|
362
362
|
const itemNames = Array.isArray(p.itemNames) && p.itemNames.length
|
|
363
363
|
? p.itemNames
|
|
364
|
-
: (p.itemName ? [p.itemName] : []);
|
|
364
|
+
: (typeof p.itemNames === 'string' && p.itemNames.trim() ? p.itemNames.split(',').map(s=>s.trim()).filter(Boolean) : (p.itemName ? [p.itemName] : []));
|
|
365
365
|
const itemsListHtml = itemNames.length
|
|
366
366
|
? `<div class="mb-2"><strong>Matériel</strong><ul style="margin:0.25rem 0 0 1.1rem; padding:0;">${itemNames.map(n => `<li>${String(n).replace(/</g, '<').replace(/>/g, '>')}</li>`).join('')}</ul></div>`
|
|
367
367
|
: '';
|
|
@@ -61,6 +61,12 @@
|
|
|
61
61
|
<input class="form-control" name="helloassoClientSecret" type="password">
|
|
62
62
|
</div>
|
|
63
63
|
|
|
64
|
+
<div class="mb-3">
|
|
65
|
+
<label class="form-label">Webhook signatureKey (x-ha-signature)</label>
|
|
66
|
+
<input class="form-control" name="helloassoSignatureKey" type="password">
|
|
67
|
+
<div class="form-text">Clé de signature HelloAsso utilisée pour vérifier le header <code>x-ha-signature</code> des webhooks. Recommandé.</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
64
70
|
<div class="mb-3">
|
|
65
71
|
<label class="form-label">Organization Slug</label>
|
|
66
72
|
<input class="form-control" name="helloassoOrganizationSlug">
|