nodebb-plugin-onekite-calendar 2.0.31 → 2.0.32

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/CHANGELOG.md CHANGED
@@ -1,10 +1,5 @@
1
1
  # Changelog – calendar-onekite
2
2
 
3
- ## 1.3.28
4
- - Perf (temps réel) : debounce global des `refetchEvents()` (calendrier + widget) et **skip** quand l’onglet est masqué (refetch unique au retour de visibilité).
5
- - Perf/Robustesse (multi-instance) : ajout d’un **lock distribué Redis** sur le tick scheduler (un seul runner exécute le cycle à un instant donné).
6
- - Perf (API events) : cache Redis très court (**TTL 2s**) par utilisateur/permissions sur l’endpoint events pour absorber les rafales.
7
-
8
3
  ## 1.3.27
9
4
  - Refactor : ajout d'un module `lib/utils.js` (format dates FR, lecture settings ACP, helpers listes/UIDs) pour supprimer les duplications.
10
5
  - Cleanup : suppression d'un doublon de fonctions dans `helloassoWebhook.js` (formatFR + getReservationIdFromPayload).
package/lib/api.js CHANGED
@@ -9,8 +9,6 @@ const user = require.main.require('./src/user');
9
9
  const groups = require.main.require('./src/groups');
10
10
  const db = require.main.require('./src/database');
11
11
  const logger = require.main.require('./src/logger');
12
- let cache = null;
13
- try { cache = require.main.require('./src/cache'); } catch (e) { cache = null; }
14
12
 
15
13
  const dbLayer = require('./db');
16
14
  const { formatFR } = require('./utils');
@@ -479,42 +477,6 @@ function computeEtag(payload) {
479
477
  return `W/"${hash}"`;
480
478
  }
481
479
 
482
- async function cacheGet(key) {
483
- try {
484
- if (!cache) return null;
485
- if (typeof cache.get === 'function') {
486
- if (cache.get.length >= 2) {
487
- return await new Promise((resolve) => {
488
- cache.get(key, (err, data) => resolve(err ? null : data));
489
- });
490
- }
491
- return await cache.get(key);
492
- }
493
- } catch (e) {}
494
- return null;
495
- }
496
-
497
- async function cacheSet(key, value, ttlSeconds) {
498
- try {
499
- if (!cache) return;
500
- const ttl = Math.max(1, parseInt(ttlSeconds, 10) || 2);
501
- if (typeof cache.set === 'function') {
502
- if (cache.set.length >= 4) {
503
- await new Promise((resolve) => {
504
- cache.set(key, value, ttl, (err) => resolve(!err));
505
- });
506
- return;
507
- }
508
- // Some NodeBB versions accept an options object.
509
- try {
510
- await cache.set(key, value, { ttl });
511
- return;
512
- } catch (e) {}
513
- await cache.set(key, value, ttl);
514
- }
515
- } catch (e) {}
516
- }
517
-
518
480
  api.getEvents = async function (req, res) {
519
481
  const qStartRaw = (req && req.query && req.query.start !== undefined) ? String(req.query.start).trim() : '';
520
482
  const qEndRaw = (req && req.query && req.query.end !== undefined) ? String(req.query.end).trim() : '';
@@ -534,22 +496,6 @@ api.getEvents = async function (req, res) {
534
496
  const canSpecialCreate = req.uid ? await canCreateSpecial(req.uid, settings) : false;
535
497
  const canSpecialDelete = req.uid ? await canDeleteSpecial(req.uid, settings) : false;
536
498
 
537
- // Ultra-short Redis-backed cache (2s) to absorb bursts and reduce redundant renders.
538
- // Keyed per uid + permissions because payload can include private fields.
539
- const uidKey = req.uid ? String(req.uid) : '0';
540
- const cacheKey = `calendar-onekite:events:${uidKey}:${canMod ? 'm' : 'u'}:${widgetMode ? 'w' : 'p'}:${qStartYmd || startTs}:${qEndYmd || endTs}`;
541
- try {
542
- const cached = await cacheGet(cacheKey);
543
- if (cached && cached.payload && cached.etag) {
544
- res.setHeader('ETag', cached.etag);
545
- res.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
546
- if (String(req.headers['if-none-match'] || '') === String(cached.etag)) {
547
- return res.status(304).end();
548
- }
549
- return res.json(cached.payload);
550
- }
551
- } catch (e) {}
552
-
553
499
  // Fetch a wider window because an event can start before the query range
554
500
  // and still overlap.
555
501
  const wideStart = Math.max(0, startTs - 366 * 24 * 3600 * 1000);
@@ -688,12 +634,6 @@ api.getEvents = async function (req, res) {
688
634
  });
689
635
 
690
636
  const etag = computeEtag(out);
691
-
692
- // Store in cache for a very short period (burst absorption).
693
- try {
694
- await cacheSet(cacheKey, { etag, payload: out }, 2);
695
- } catch (e) {}
696
-
697
637
  res.setHeader('ETag', etag);
698
638
  res.setHeader('Cache-Control', 'private, max-age=0, must-revalidate');
699
639
  if (String(req.headers['if-none-match'] || '') === etag) {
package/lib/scheduler.js CHANGED
@@ -8,43 +8,9 @@ const realtime = require('./realtime');
8
8
  const nconf = require.main.require('nconf');
9
9
  const groups = require.main.require('./src/groups');
10
10
  const utils = require('./utils');
11
- let redis = null;
12
- try { redis = require.main.require('./src/redis'); } catch (e) { redis = null; }
13
11
 
14
12
  let timer = null;
15
13
 
16
- // Distributed lock (Redis) to avoid running the scheduler on multiple NodeBB instances.
17
- // Safe no-op if Redis is not available.
18
- async function acquireSchedulerLock(ttlMs) {
19
- const key = 'calendar-onekite:scheduler:lock';
20
- const value = `${process.pid}:${Date.now()}`;
21
- const ttl = Math.max(1000, parseInt(ttlMs, 10) || 55000);
22
-
23
- // Prefer Redis SET NX PX.
24
- try {
25
- const client = redis && (redis.client || redis);
26
- if (client && typeof client.set === 'function') {
27
- const res = await new Promise((resolve, reject) => {
28
- // node_redis style
29
- client.set(key, value, 'NX', 'PX', ttl, (err, out) => {
30
- if (err) return reject(err);
31
- resolve(out);
32
- });
33
- });
34
- return String(res || '').toUpperCase() === 'OK';
35
- }
36
- } catch (e) {
37
- // ignore and fall back
38
- }
39
-
40
- // Fallback: in-process lock only.
41
- if (global.__onekiteSchedulerInProcessLockUntil && Date.now() < global.__onekiteSchedulerInProcessLockUntil) {
42
- return false;
43
- }
44
- global.__onekiteSchedulerInProcessLockUntil = Date.now() + ttl;
45
- return true;
46
- }
47
-
48
14
  // Some NodeBB database adapters don't expose setAdd/setRemove helpers.
49
15
  // Use a safe "add once" guard to avoid crashing the scheduler.
50
16
  async function addOnce(key, value) {
@@ -434,14 +400,13 @@ function start() {
434
400
  // eslint-disable-next-line no-console
435
401
  console.info('[calendar-onekite] Scheduler enabled');
436
402
  timer = setInterval(() => {
437
- (async () => {
438
- const locked = await acquireSchedulerLock(55 * 1000);
439
- if (!locked) return;
440
- await expirePending();
441
- await processAwaitingPayment();
442
- })().catch((err) => {
403
+ expirePending().catch((err) => {
404
+ // eslint-disable-next-line no-console
405
+ console.warn('[calendar-onekite] Scheduler error in expirePending', err && err.message ? err.message : err);
406
+ });
407
+ processAwaitingPayment().catch((err) => {
443
408
  // eslint-disable-next-line no-console
444
- console.warn('[calendar-onekite] Scheduler tick error', err && err.message ? err.message : err);
409
+ console.warn('[calendar-onekite] Scheduler error in processAwaitingPayment', err && err.message ? err.message : err);
445
410
  });
446
411
  }, 60 * 1000);
447
412
  }
package/lib/widgets.js CHANGED
@@ -376,20 +376,13 @@ dateClick: function() { window.location.href = calUrl; },
376
376
  let tRefetch = null;
377
377
  const refetch = function () {
378
378
  try {
379
- // Skip work while tab is hidden; refetch once when visible again.
380
- try {
381
- if (typeof document !== 'undefined' && document && document.hidden) {
382
- window.__oneKiteWidgetNeedsRefetch = true;
383
- return;
384
- }
385
- } catch (e) {}
386
379
  if (tRefetch) return;
387
380
  tRefetch = setTimeout(() => {
388
381
  tRefetch = null;
389
382
  try {
390
383
  calendar.refetchEvents();
391
384
  } catch (e) {}
392
- }, 450);
385
+ }, 200);
393
386
  } catch (e) {}
394
387
  };
395
388
 
@@ -409,21 +402,6 @@ dateClick: function() { window.location.href = calUrl; },
409
402
  };
410
403
  socket.on('event:calendar-onekite.calendarUpdated', triggerAll);
411
404
  socket.on('event:calendar-onekite.reservationUpdated', triggerAll);
412
-
413
- // If updates occurred while hidden, refetch once when visible again.
414
- try {
415
- if (!window.__oneKiteWidgetVisibilityBound && typeof document !== 'undefined' && document && typeof document.addEventListener === 'function') {
416
- window.__oneKiteWidgetVisibilityBound = true;
417
- document.addEventListener('visibilitychange', () => {
418
- try {
419
- if (!document.hidden && window.__oneKiteWidgetNeedsRefetch) {
420
- window.__oneKiteWidgetNeedsRefetch = false;
421
- triggerAll();
422
- }
423
- } catch (e) {}
424
- }, { passive: true });
425
- }
426
- } catch (e) {}
427
405
  }
428
406
  } catch (e) {}
429
407
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-onekite-calendar",
3
- "version": "2.0.31",
3
+ "version": "2.0.32",
4
4
  "description": "FullCalendar-based equipment reservation workflow with admin approval & HelloAsso payment for NodeBB",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
@@ -533,40 +533,13 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
533
533
  function scheduleRefetch(cal) {
534
534
  try {
535
535
  if (!cal || typeof cal.refetchEvents !== 'function') return;
536
-
537
- // Avoid refetching while the tab is hidden; do it once when visible again.
538
- try {
539
- if (typeof document !== 'undefined' && document && document.hidden) {
540
- window.__onekiteNeedsRefetch = true;
541
- return;
542
- }
543
- } catch (e) {}
544
-
545
536
  clearTimeout(window.__onekiteRefetchTimer);
546
537
  window.__onekiteRefetchTimer = setTimeout(() => {
547
538
  try { cal.refetchEvents(); } catch (e) {}
548
- }, 450);
539
+ }, 150);
549
540
  } catch (e) {}
550
541
  }
551
542
 
552
- // If updates occurred while hidden, refetch once when visible again.
553
- try {
554
- if (!window.__onekiteVisibilityBound && typeof document !== 'undefined' && document && typeof document.addEventListener === 'function') {
555
- window.__onekiteVisibilityBound = true;
556
- document.addEventListener('visibilitychange', () => {
557
- try {
558
- if (!document.hidden && window.__onekiteNeedsRefetch) {
559
- window.__onekiteNeedsRefetch = false;
560
- const cal = window.oneKiteCalendar;
561
- if (cal && typeof cal.refetchEvents === 'function') {
562
- scheduleRefetch(cal);
563
- }
564
- }
565
- } catch (e) {}
566
- }, { passive: true });
567
- }
568
- } catch (e) {}
569
-
570
543
  async function fetchJsonCached(url, opts) {
571
544
  const cached = jsonCache.get(url);
572
545
  const headers = Object.assign({}, (opts && opts.headers) || {});
package/plugin.json CHANGED
@@ -39,5 +39,5 @@
39
39
  "acpScripts": [
40
40
  "public/admin.js"
41
41
  ],
42
- "version": "2.0.31"
42
+ "version": "2.0.32"
43
43
  }
package/public/client.js CHANGED
@@ -543,40 +543,13 @@ define('forum/calendar-onekite', ['alerts', 'bootbox', 'hooks'], function (alert
543
543
  function scheduleRefetch(cal) {
544
544
  try {
545
545
  if (!cal || typeof cal.refetchEvents !== 'function') return;
546
-
547
- // Avoid refetching while the tab is hidden; do it once when visible again.
548
- try {
549
- if (typeof document !== 'undefined' && document && document.hidden) {
550
- window.__onekiteNeedsRefetch = true;
551
- return;
552
- }
553
- } catch (e) {}
554
-
555
546
  clearTimeout(window.__onekiteRefetchTimer);
556
547
  window.__onekiteRefetchTimer = setTimeout(() => {
557
548
  try { cal.refetchEvents(); } catch (e) {}
558
- }, 450);
549
+ }, 150);
559
550
  } catch (e) {}
560
551
  }
561
552
 
562
- // If updates occurred while hidden, refetch once when visible again.
563
- try {
564
- if (!window.__onekiteVisibilityBound && typeof document !== 'undefined' && document && typeof document.addEventListener === 'function') {
565
- window.__onekiteVisibilityBound = true;
566
- document.addEventListener('visibilitychange', () => {
567
- try {
568
- if (!document.hidden && window.__onekiteNeedsRefetch) {
569
- window.__onekiteNeedsRefetch = false;
570
- const cal = window.oneKiteCalendar;
571
- if (cal && typeof cal.refetchEvents === 'function') {
572
- scheduleRefetch(cal);
573
- }
574
- }
575
- } catch (e) {}
576
- }, { passive: true });
577
- }
578
- } catch (e) {}
579
-
580
553
  async function fetchJsonCached(url, opts) {
581
554
  const cached = jsonCache.get(url);
582
555
  const headers = Object.assign({}, (opts && opts.headers) || {});