nodebb-plugin-equipment-calendar 9.0.13 → 9.0.14
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 +0 -78
- package/package.json +1 -1
- package/plugin.json +1 -1
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);
|
|
@@ -759,7 +695,6 @@ router.get('/admin/plugins/equipment-calendar', middleware.applyCSRF, mid.admin.
|
|
|
759
695
|
// Convenience alias (optional): /calendar -> /equipment/calendar
|
|
760
696
|
router.get('/calendar', (req, res) => res.redirect('/equipment/calendar'));
|
|
761
697
|
|
|
762
|
-
|
|
763
698
|
// To verify webhook signature we need raw body; add a rawBody collector for this route only
|
|
764
699
|
router.post('/equipment/webhook/helloasso',
|
|
765
700
|
require.main.require('body-parser').text({ type: '*/*' }),
|
|
@@ -870,7 +805,6 @@ plugin.addAdminRoutes = async function (params) {
|
|
|
870
805
|
router.get('/api/admin/plugins/equipment-calendar', middleware.applyCSRF, renderAdminPage);
|
|
871
806
|
};
|
|
872
807
|
|
|
873
|
-
|
|
874
808
|
async function renderAdminReservationsPage(req, res) {
|
|
875
809
|
if (!(await ensureIsAdmin(req, res))) return;
|
|
876
810
|
|
|
@@ -995,7 +929,6 @@ async function handleAdminDelete(req, res) {
|
|
|
995
929
|
return res.redirect('/admin/plugins/equipment-calendar/reservations?updated=1');
|
|
996
930
|
}
|
|
997
931
|
|
|
998
|
-
|
|
999
932
|
async function handleHelloAssoTest(req, res) {
|
|
1000
933
|
const isAdmin = req.uid ? await groups.isMember(req.uid, 'administrators') : false;
|
|
1001
934
|
if (!isAdmin) return helpers.notAllowed(req, res);
|
|
@@ -1196,11 +1129,8 @@ async function renderCalendarPage(req, res) {
|
|
|
1196
1129
|
});
|
|
1197
1130
|
}
|
|
1198
1131
|
|
|
1199
|
-
|
|
1200
1132
|
// --- Approvals page ---
|
|
1201
1133
|
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
1134
|
async function notifyApprovers(reservations, settings) {
|
|
1205
1135
|
const groupName = (settings.notifyGroup || settings.approverGroup || 'administrators').trim();
|
|
1206
1136
|
if (!groupName) return;
|
|
@@ -1431,8 +1361,6 @@ async function handleCreateReservation(req, res) {
|
|
|
1431
1361
|
}
|
|
1432
1362
|
}
|
|
1433
1363
|
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
1364
|
async function handleApproveReservation(req, res) {
|
|
1437
1365
|
try {
|
|
1438
1366
|
const settings = await getSettings();
|
|
@@ -1512,9 +1440,6 @@ async function handleRejectReservation(req, res) {
|
|
|
1512
1440
|
}
|
|
1513
1441
|
}
|
|
1514
1442
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
1443
|
async function ensureIsAdmin(req, res) {
|
|
1519
1444
|
const isAdmin = req.uid ? await groups.isMember(req.uid, 'administrators') : false;
|
|
1520
1445
|
if (!isAdmin) {
|
|
@@ -1593,8 +1518,6 @@ async function handleAdminPurge(req, res) {
|
|
|
1593
1518
|
}
|
|
1594
1519
|
}
|
|
1595
1520
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
1521
|
let paymentTimeoutInterval = null;
|
|
1599
1522
|
|
|
1600
1523
|
function startPaymentTimeoutScheduler() {
|
|
@@ -1646,7 +1569,6 @@ function startPaymentTimeoutScheduler() {
|
|
|
1646
1569
|
|
|
1647
1570
|
module.exports = plugin;
|
|
1648
1571
|
|
|
1649
|
-
|
|
1650
1572
|
async function handleGetReservation(req, res) {
|
|
1651
1573
|
if (!req.uid) return res.status(403).json({ error: 'not-logged-in' });
|
|
1652
1574
|
const rid = String(req.params.id || '');
|
package/package.json
CHANGED