nodebb-plugin-onekite-calendar 2.0.66 → 2.0.67

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.
@@ -1,179 +1,16 @@
1
1
  'use strict';
2
2
 
3
- const nconf = require.main.require('nconf');
4
- const meta = require.main.require('./src/meta');
5
- const emailer = require.main.require('./src/emailer');
6
- const groups = require.main.require('./src/groups');
7
- const user = require.main.require('./src/user');
8
-
9
- const { getGroupNameBySlug } = require('./group-helpers');
10
-
11
- function normalizeAllowedGroups(raw) {
12
- if (!raw) return [];
13
- if (Array.isArray(raw)) {
14
- return raw.map((v) => String(v || '').trim()).filter(Boolean);
15
- }
16
- const s = String(raw || '').trim();
17
- if (!s) return [];
18
-
19
- if (s.startsWith('[') && s.endsWith(']')) {
20
- try {
21
- const parsed = JSON.parse(s);
22
- if (Array.isArray(parsed)) {
23
- return parsed.map((v) => String(v || '').trim()).filter(Boolean);
24
- }
25
- } catch (e) {
26
- // fall through
27
- }
28
- }
29
-
30
- return s
31
- .split(',')
32
- .map((v) => String(v || '').trim().replace(/^"+|"+$/g, ''))
33
- .filter(Boolean);
34
- }
35
-
36
- function normalizeUids(members) {
37
- if (!Array.isArray(members)) return [];
38
- const out = [];
39
- for (const m of members) {
40
- if (Number.isInteger(m)) {
41
- out.push(m);
42
- continue;
43
- }
44
- if (typeof m === 'string' && m.trim()) {
45
- const n = parseInt(m, 10);
46
- if (Number.isFinite(n)) out.push(n);
47
- continue;
48
- }
49
- if (m && typeof m === 'object' && m.uid != null) {
50
- const n = Number.isInteger(m.uid) ? m.uid : parseInt(String(m.uid).trim(), 10);
51
- if (Number.isFinite(n)) out.push(n);
52
- }
53
- }
54
- return Array.from(new Set(out)).filter((u) => Number.isInteger(u) && u > 0);
55
- }
56
-
57
- async function getMembersByGroupIdentifier(groupIdentifier) {
58
- const id = String(groupIdentifier || '').trim();
59
- if (!id) return [];
60
-
61
- try {
62
- const members = await groups.getMembers(id, 0, -1);
63
- if (Array.isArray(members) && members.length) return members;
64
- } catch (e) {
65
- // ignore
66
- }
67
-
68
- const groupName = await getGroupNameBySlug(id);
69
- if (groupName && String(groupName).trim() && String(groupName).trim() !== id) {
70
- try {
71
- const members = await groups.getMembers(String(groupName).trim(), 0, -1);
72
- return Array.isArray(members) ? members : [];
73
- } catch (e) {
74
- return [];
75
- }
76
- }
77
-
78
- return [];
79
- }
80
-
81
- async function userInAnyGroup(uid, allowedGroups) {
82
- const allowed = normalizeAllowedGroups(allowedGroups);
83
- if (!uid || !allowed.length) return false;
84
-
85
- const ug = await groups.getUserGroups([uid]);
86
- const list = (ug && ug[0]) ? ug[0] : [];
87
-
88
- const seen = new Set();
89
- for (const g of list) {
90
- if (!g) continue;
91
- if (g.slug) seen.add(String(g.slug));
92
- if (g.name) seen.add(String(g.name));
93
- if (g.groupName) seen.add(String(g.groupName));
94
- if (g.displayName) seen.add(String(g.displayName));
95
- }
96
-
97
- return allowed.some((v) => seen.has(String(v)));
98
- }
99
-
100
- function forumBaseUrl() {
101
- let base = (meta && meta.config && (meta.config.url || meta.config['url'])) ? (meta.config.url || meta.config['url']) : '';
102
- if (!base) base = String(nconf.get('url') || '').trim();
103
- base = String(base || '').trim().replace(/\/$/, '');
104
- if (base && !/^https?:\/\//i.test(base)) {
105
- base = `https://${base.replace(/^\/\//, '')}`;
106
- }
107
- return base;
108
- }
109
-
110
- async function sendEmail(template, uid, subject, params) {
111
- const toUid = Number.isInteger(uid) ? uid : parseInt(String(uid || '').trim(), 10);
112
- if (!Number.isFinite(toUid) || toUid <= 0) return;
113
-
114
- const payload = Object.assign({}, params || {}, subject ? { subject } : {});
115
-
116
- try {
117
- if (typeof emailer.send !== 'function') return;
118
- await emailer.send(template, toUid, payload);
119
- } catch (err) {
120
- // eslint-disable-next-line no-console
121
- console.warn('[calendar-onekite] Failed to send email', { template, uid: toUid, err: String((err && err.message) || err) });
122
- }
123
- }
124
-
125
- function normalizeUidList(uids) {
126
- if (!uids) return [];
127
- const arr = Array.isArray(uids) ? uids : (() => {
128
- const s = String(uids || '').trim();
129
- if (!s) return [];
130
- try {
131
- const parsed = JSON.parse(s);
132
- return Array.isArray(parsed) ? parsed : [];
133
- } catch (e) {
134
- return s.split(',').map((x) => String(x).trim()).filter(Boolean);
135
- }
136
- })();
137
-
138
- const out = [];
139
- for (const u of arr) {
140
- const n = Number.isInteger(u) ? u : parseInt(String(u || '').trim(), 10);
141
- if (Number.isFinite(n) && n > 0) out.push(String(n));
142
- }
143
- return Array.from(new Set(out));
144
- }
145
-
146
- async function usernamesByUids(uids) {
147
- const ids = normalizeUidList(uids);
148
- if (!ids.length) return [];
149
-
150
- try {
151
- if (typeof user.getUsersFields === 'function') {
152
- const rows = await user.getUsersFields(ids, ['username']);
153
- return (rows || []).map((r) => (r && r.username ? String(r.username) : '')).filter(Boolean);
154
- }
155
- } catch (e) {
156
- // fall through
157
- }
158
-
159
- const rows = await Promise.all(ids.map(async (uid) => {
160
- try {
161
- const r = await user.getUserFields(uid, ['username']);
162
- return r && r.username ? String(r.username) : '';
163
- } catch (e) {
164
- return '';
165
- }
166
- }));
167
- return rows.filter(Boolean);
168
- }
3
+ // Thin re-export layer for backward compatibility.
4
+ // All helpers now live in ./shared.js.
5
+ const shared = require('./shared');
169
6
 
170
7
  module.exports = {
171
- normalizeAllowedGroups,
172
- normalizeUids,
173
- getMembersByGroupIdentifier,
174
- userInAnyGroup,
175
- forumBaseUrl,
176
- sendEmail,
177
- normalizeUidList,
178
- usernamesByUids,
8
+ normalizeAllowedGroups: shared.normalizeAllowedGroups,
9
+ normalizeUids: shared.normalizeUids,
10
+ getMembersByGroupIdentifier: shared.getMembersByGroupIdentifier,
11
+ userInAnyGroup: shared.userInAnyGroup,
12
+ forumBaseUrl: shared.forumBaseUrl,
13
+ sendEmail: shared.sendEmail,
14
+ normalizeUidList: shared.normalizeUidList,
15
+ usernamesByUids: shared.usernamesByUids,
179
16
  };
package/lib/scheduler.js CHANGED
@@ -1,15 +1,23 @@
1
1
  'use strict';
2
2
 
3
- const meta = require.main.require('./src/meta');
3
+ const nconf = require.main.require('nconf');
4
4
  const db = require.main.require('./src/database');
5
5
  const dbLayer = require('./db');
6
6
  const discord = require('./discord');
7
7
  const realtime = require('./realtime');
8
- const nconf = require.main.require('nconf');
9
- const utils = require("./utils");
10
- const nb = require("./nodebb-helpers");
11
8
  const { getSettings } = require('./settings');
12
9
 
10
+ const shared = require('./shared');
11
+ const {
12
+ getSetting,
13
+ formatFR,
14
+ arrayifyNames,
15
+ forumBaseUrl,
16
+ normalizeAllowedGroups,
17
+ normalizeUids,
18
+ getMembersByGroupIdentifier,
19
+ sendEmail,
20
+ } = shared;
13
21
  let timer = null;
14
22
 
15
23
  // Some NodeBB database adapters don't expose setAdd/setRemove helpers.
@@ -53,8 +61,7 @@ async function addOnce(key, value) {
53
61
  }
54
62
  }
55
63
 
56
- const { getSetting, formatFR, arrayifyNames } = utils;
57
- const { forumBaseUrl, normalizeAllowedGroups, normalizeUids, getMembersByGroupIdentifier, sendEmail } = nb;
64
+ // Helpers imported from shared.js above
58
65
  async function getValidatorUids(settings) {
59
66
  const out = new Set();
60
67
  // Always include administrators
@@ -96,7 +103,7 @@ async function getNotifiedValidatorUids(settings) {
96
103
  }
97
104
 
98
105
  // Pending holds: short lock after a user creates a request (defaults to 5 minutes)
99
- async function expirePending() {
106
+ async function expirePending(preIds, preReservations) {
100
107
  const settings = await getSettings();
101
108
  const holdMins = parseInt(getSetting(settings, 'pendingHoldMinutes', '5'), 10) || 5;
102
109
  const validatorReminderMins = parseInt(getSetting(settings, 'validatorReminderMinutesPending', '0'), 10) || 0;
@@ -111,11 +118,10 @@ async function expirePending() {
111
118
 
112
119
  const validatorUids = validatorReminderMins > 0 ? await getNotifiedValidatorUids(settings) : [];
113
120
 
114
- const ids = await dbLayer.listAllReservationIds(5000);
121
+ const ids = preIds || await dbLayer.listAllReservationIds(5000);
115
122
  if (!ids || !ids.length) return;
116
123
 
117
- // Batch load to avoid N+1 queries on Mongo/Redis adapters.
118
- const reservations = await dbLayer.getReservations(ids);
124
+ const reservations = preReservations || await dbLayer.getReservations(ids);
119
125
 
120
126
  for (let i = 0; i < ids.length; i += 1) {
121
127
  const rid = ids[i];
@@ -197,7 +203,7 @@ async function expirePending() {
197
203
  // - When a reservation is validated it becomes awaiting_payment
198
204
  // - We send a reminder after `paymentHoldMinutes` (default 60)
199
205
  // - We expire (and remove) after `2 * paymentHoldMinutes`
200
- async function processAwaitingPayment() {
206
+ async function processAwaitingPayment(preIds, preReservations) {
201
207
  const settings = await getSettings();
202
208
  const holdMins = parseInt(
203
209
  getSetting(settings, 'paymentHoldMinutes', getSetting(settings, 'holdMinutes', '60')),
@@ -205,7 +211,7 @@ async function processAwaitingPayment() {
205
211
  ) || 60;
206
212
  const now = Date.now();
207
213
 
208
- const ids = await dbLayer.listAllReservationIds(5000);
214
+ const ids = preIds || await dbLayer.listAllReservationIds(5000);
209
215
  if (!ids || !ids.length) return;
210
216
 
211
217
  const user = require.main.require('./src/user');
@@ -215,7 +221,7 @@ async function processAwaitingPayment() {
215
221
  return base ? `${base}/admin/plugins/calendar-onekite` : '/admin/plugins/calendar-onekite';
216
222
  })();
217
223
 
218
- const reservations = await dbLayer.getReservations(ids);
224
+ const reservations = preReservations || await dbLayer.getReservations(ids);
219
225
 
220
226
  for (let i = 0; i < ids.length; i += 1) {
221
227
  const rid = ids[i];
@@ -339,15 +345,20 @@ function start() {
339
345
  if (timer) return;
340
346
  // eslint-disable-next-line no-console
341
347
  console.info('[calendar-onekite] Scheduler enabled');
342
- timer = setInterval(() => {
343
- expirePending().catch((err) => {
344
- // eslint-disable-next-line no-console
345
- console.warn('[calendar-onekite] Scheduler error in expirePending', err && err.message ? err.message : err);
346
- });
347
- processAwaitingPayment().catch((err) => {
348
- // eslint-disable-next-line no-console
349
- console.warn('[calendar-onekite] Scheduler error in processAwaitingPayment', err && err.message ? err.message : err);
350
- });
348
+ timer = setInterval(async () => {
349
+ try {
350
+ // Single DB fetch shared between both jobs (avoids duplicate listAll + getReservations)
351
+ const ids = await dbLayer.listAllReservationIds(5000);
352
+ const reservations = ids && ids.length ? await dbLayer.getReservations(ids) : [];
353
+ await expirePending(ids, reservations).catch((err) => {
354
+ console.warn('[calendar-onekite] Scheduler error in expirePending', err && err.message ? err.message : err);
355
+ });
356
+ await processAwaitingPayment(ids, reservations).catch((err) => {
357
+ console.warn('[calendar-onekite] Scheduler error in processAwaitingPayment', err && err.message ? err.message : err);
358
+ });
359
+ } catch (err) {
360
+ console.warn('[calendar-onekite] Scheduler tick error', err && err.message ? err.message : err);
361
+ }
351
362
  }, 60 * 1000);
352
363
  }
353
364