nodebb-plugin-equipment-calendar 9.0.13 → 9.0.15

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/library.js CHANGED
@@ -28,7 +28,6 @@ function generateId() {
28
28
  }
29
29
  }
30
30
 
31
-
32
31
  function normalizeItemIds(itemIdsRaw) {
33
32
  if (!itemIdsRaw) return [];
34
33
  if (Array.isArray(itemIdsRaw)) {
@@ -45,7 +44,6 @@ function normalizeItemIds(itemIdsRaw) {
45
44
  }
46
45
  }
47
46
 
48
-
49
47
  function parseDateInput(v) {
50
48
  if (v === null || v === undefined) return null;
51
49
  if (typeof v === 'number' || (typeof v === 'string' && v.trim() && /^\d+$/.test(v.trim()))) {
@@ -71,7 +69,6 @@ function parseDateInput(v) {
71
69
  }
72
70
  }
73
71
 
74
-
75
72
  function tzOffsetMs(date, timeZone) {
76
73
  const asTz = new Date(date.toLocaleString('en-US', { timeZone }));
77
74
  return asTz.getTime() - date.getTime();
@@ -100,8 +97,6 @@ function addDaysIso(iso, days) {
100
97
  return dt.getUTCFullYear() + '-' + pad(dt.getUTCMonth() + 1) + '-' + pad(dt.getUTCDate());
101
98
  }
102
99
 
103
-
104
-
105
100
  function formatDateTimeFR(ms) {
106
101
  const n = Number(ms || 0);
107
102
  if (!n) return '';
@@ -124,8 +119,6 @@ function formatDateTimeFR(ms) {
124
119
  }
125
120
  }
126
121
 
127
-
128
-
129
122
  const axios = require('axios');
130
123
  const { DateTime } = require('luxon');
131
124
  const { v4: uuidv4 } = require('uuid');
@@ -190,53 +183,6 @@ async function setSettings(payload) {
190
183
  Object.keys(payload || {}).forEach((k) => {
191
184
  sets[k] = JSON.stringify(payload[k]);
192
185
  });
193
- await meta.settings.set('equipmentCalendar', sets);
194
- }
195
- function parseLocationMap(locationMapJson) {
196
- try {
197
- const obj = JSON.parse(locationMapJson || '{}');
198
- return (obj && typeof obj === 'object') ? obj : {};
199
- } catch (e) {
200
- return {};
201
- }
202
- }
203
-
204
- let haTokenCache = null; // { accessToken, refreshToken, expMs }
205
-
206
- async function getHelloAssoAccessToken(settings, opts = {}) {
207
- const now = Date.now();
208
- if (!opts.force && haTokenCache && haTokenCache.accessToken && haTokenCache.expMs && now < haTokenCache.expMs - 30_000) {
209
- return haTokenCache.accessToken;
210
- }
211
-
212
- const tokenKey = 'equipmentCalendar:ha:token';
213
- if (opts.clearStored) {
214
- try { await db.delete(tokenKey); } catch (e) {}
215
- }
216
- let stored = null;
217
- try {
218
- stored = await db.getObject(tokenKey);
219
- } catch (e) {}
220
-
221
- // If refresh token exists and not expired locally, try refresh flow first
222
- const canRefresh = !opts.force && stored && stored.refresh_token;
223
- const useRefresh = canRefresh && stored.refresh_expires_at && now < parseInt(stored.refresh_expires_at, 10);
224
-
225
- const formBody = new URLSearchParams();
226
- if (useRefresh) {
227
- formBody.set('grant_type', 'refresh_token');
228
- formBody.set('refresh_token', stored.refresh_token);
229
- } else {
230
- formBody.set('grant_type', 'client_credentials');
231
- formBody.set('client_id', String(settings.ha_clientId || ''));
232
- formBody.set('client_secret', String(settings.ha_clientSecret || ''));
233
- }
234
-
235
- const resp = await fetchFn('https://api.helloasso.com/oauth2/token', {
236
- method: 'POST',
237
- headers: { 'content-type': 'application/x-www-form-urlencoded' },
238
- body: formBody.toString(),
239
- });
240
186
  if (!resp.ok) {
241
187
  const t = await resp.text();
242
188
  throw new Error(`HelloAsso token error: ${resp.status} ${t}`);
@@ -263,7 +209,6 @@ async function getHelloAssoAccessToken(settings, opts = {}) {
263
209
  return accessToken;
264
210
  }
265
211
 
266
-
267
212
  async function createHelloAssoCheckoutIntent(settings, bookingId, reservations) {
268
213
  const org = String(settings.ha_organizationSlug || '').trim();
269
214
  if (!org) throw new Error('HelloAsso organization slug missing');
@@ -350,7 +295,6 @@ function isCheckoutPaid(checkout) {
350
295
  return false;
351
296
  }
352
297
 
353
-
354
298
  async function fetchHelloAssoItems(settings) {
355
299
  const org = String(settings.ha_organizationSlug || '').trim();
356
300
  const formType = String(settings.ha_itemsFormType || '').trim();
@@ -424,8 +368,6 @@ async function fetchHelloAssoItems(settings) {
424
368
  return out;
425
369
  }
426
370
 
427
-
428
-
429
371
  async function getActiveItems() {
430
372
  const settings = await getSettings();
431
373
  const rawItems = await fetchHelloAssoItems(settings);
@@ -438,7 +380,6 @@ async function getActiveItems() {
438
380
  return items;
439
381
  }
440
382
 
441
-
442
383
  // --- Data layer ---
443
384
  // Keys:
444
385
  // item hash: equipmentCalendar:items (stored in settings as JSON)
@@ -455,9 +396,6 @@ function statusBlocksItem(status) {
455
396
  return (s === 'pending' || s === 'approved' || s === 'payment_pending' || s === 'paid');
456
397
  }
457
398
 
458
-
459
-
460
-
461
399
  async function saveBooking(booking) {
462
400
  const bid = booking.bookingId;
463
401
  await db.setObject(`equipmentCalendar:booking:${bid}`, booking);
@@ -490,7 +428,6 @@ async function getReservation(rid) {
490
428
  return await db.getObject(resKey(id));
491
429
  }
492
430
 
493
-
494
431
  async function setReservationStatus(rid, status) {
495
432
  const r = await getReservation(rid);
496
433
  if (!r) throw new Error('not_found');
@@ -509,7 +446,6 @@ async function deleteReservation(rid) {
509
446
  await db.delete(resKey(rid));
510
447
  }
511
448
 
512
-
513
449
  function normalizeReservation(obj) {
514
450
  const startMs = Number(obj.startMs);
515
451
  const endMs = Number(obj.endMs);
@@ -736,13 +672,8 @@ plugin.init = async function (params) {
736
672
  // Admin (ACP) routes
737
673
  if (mid && mid.admin) {
738
674
 
739
- await meta.settings.set('equipmentCalendar', out);
740
- return res.redirect(nconf.get('relative_path') + '/admin/plugins/equipment-calendar?saved=1');
741
- } catch (e) {
742
- winston.error('[equipment-calendar] admin save error', e);
743
- return res.redirect(nconf.get('relative_path') + '/admin/plugins/equipment-calendar?error=1');
744
- }
745
- });
675
+
676
+
746
677
 
747
678
  router.get('/admin/plugins/equipment-calendar', middleware.applyCSRF, mid.admin.buildHeader, renderAdminPage);
748
679
  router.get('/admin/plugins/equipment-calendar/reservations', middleware.applyCSRF, mid.admin.buildHeader, renderAdminReservationsPage);
@@ -759,7 +690,6 @@ router.get('/admin/plugins/equipment-calendar', middleware.applyCSRF, mid.admin.
759
690
  // Convenience alias (optional): /calendar -> /equipment/calendar
760
691
  router.get('/calendar', (req, res) => res.redirect('/equipment/calendar'));
761
692
 
762
-
763
693
  // To verify webhook signature we need raw body; add a rawBody collector for this route only
764
694
  router.post('/equipment/webhook/helloasso',
765
695
  require.main.require('body-parser').text({ type: '*/*' }),
@@ -870,7 +800,6 @@ plugin.addAdminRoutes = async function (params) {
870
800
  router.get('/api/admin/plugins/equipment-calendar', middleware.applyCSRF, renderAdminPage);
871
801
  };
872
802
 
873
-
874
803
  async function renderAdminReservationsPage(req, res) {
875
804
  if (!(await ensureIsAdmin(req, res))) return;
876
805
 
@@ -995,7 +924,6 @@ async function handleAdminDelete(req, res) {
995
924
  return res.redirect('/admin/plugins/equipment-calendar/reservations?updated=1');
996
925
  }
997
926
 
998
-
999
927
  async function handleHelloAssoTest(req, res) {
1000
928
  const isAdmin = req.uid ? await groups.isMember(req.uid, 'administrators') : false;
1001
929
  if (!isAdmin) return helpers.notAllowed(req, res);
@@ -1196,11 +1124,8 @@ async function renderCalendarPage(req, res) {
1196
1124
  });
1197
1125
  }
1198
1126
 
1199
-
1200
1127
  // --- Approvals page ---
1201
1128
 
1202
-
1203
-
1204
1129
  async function notifyApprovers(reservations, settings) {
1205
1130
  const groupName = (settings.notifyGroup || settings.approverGroup || 'administrators').trim();
1206
1131
  if (!groupName) return;
@@ -1431,8 +1356,6 @@ async function handleCreateReservation(req, res) {
1431
1356
  }
1432
1357
  }
1433
1358
 
1434
-
1435
-
1436
1359
  async function handleApproveReservation(req, res) {
1437
1360
  try {
1438
1361
  const settings = await getSettings();
@@ -1512,9 +1435,6 @@ async function handleRejectReservation(req, res) {
1512
1435
  }
1513
1436
  }
1514
1437
 
1515
-
1516
-
1517
-
1518
1438
  async function ensureIsAdmin(req, res) {
1519
1439
  const isAdmin = req.uid ? await groups.isMember(req.uid, 'administrators') : false;
1520
1440
  if (!isAdmin) {
@@ -1593,8 +1513,6 @@ async function handleAdminPurge(req, res) {
1593
1513
  }
1594
1514
  }
1595
1515
 
1596
-
1597
-
1598
1516
  let paymentTimeoutInterval = null;
1599
1517
 
1600
1518
  function startPaymentTimeoutScheduler() {
@@ -1646,7 +1564,6 @@ function startPaymentTimeoutScheduler() {
1646
1564
 
1647
1565
  module.exports = plugin;
1648
1566
 
1649
-
1650
1567
  async function handleGetReservation(req, res) {
1651
1568
  if (!req.uid) return res.status(403).json({ error: 'not-logged-in' });
1652
1569
  const rid = String(req.params.id || '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-equipment-calendar",
3
- "version": "9.0.13",
3
+ "version": "9.0.15",
4
4
  "description": "Equipment reservation calendar for NodeBB (FullCalendar, approvals, HelloAsso payments)",
5
5
  "main": "library.js",
6
6
  "scripts": {
package/plugin.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "scripts": [
26
26
  "public/js/client.js"
27
27
  ],
28
- "version": "3.0.0-stable5a-acp-official-behavior",
28
+ "version": "3.0.0-stable5c-syntaxfix2",
29
29
  "minver": "4.7.1",
30
30
  "acpScripts": [
31
31
  "public/js/admin.js"