bulltrackers-module 1.0.667 → 1.0.668

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.
@@ -14,11 +14,69 @@ const { runFinalSweepCheck } = require('../tools/FinalSweepReporter');
14
14
  // 1. IMPORT SNAPSHOT SERVICE
15
15
  const { generateDailySnapshots } = require('../services/SnapshotService');
16
16
  const crypto = require('crypto');
17
+ // Import Google Auth Library for OAuth 2.0 authentication
18
+ let GoogleAuth = null;
19
+ try {
20
+ GoogleAuth = require('google-auth-library').GoogleAuth;
21
+ } catch (e) {
22
+ // google-auth-library might not be installed, will handle gracefully
23
+ }
17
24
 
18
25
  const BASE_SECONDS_PER_WEIGHT_UNIT = 3;
19
26
  const SESSION_CACHE_DURATION_MS = 1000 * 60 * 30; // 30 Minutes
20
27
  const STALE_LOCK_THRESHOLD_MS = 1000 * 60 * 15;
21
28
 
29
+ /**
30
+ * Helper function to send an authenticated callback to Cloud Workflows
31
+ * Uses OAuth 2.0 access token from the default service account
32
+ */
33
+ async function sendAuthenticatedCallback(callbackUrl, payload, logger, directFetch) {
34
+ if (!GoogleAuth) {
35
+ throw new Error('google-auth-library is required for authenticated callbacks. Please install: npm install google-auth-library');
36
+ }
37
+
38
+ try {
39
+ // Get OAuth 2.0 access token using default service account credentials
40
+ const auth = new GoogleAuth({
41
+ scopes: ['https://www.googleapis.com/auth/cloud-platform']
42
+ });
43
+ const accessToken = await auth.getAccessToken();
44
+
45
+ if (!accessToken) {
46
+ throw new Error('Failed to obtain OAuth 2.0 access token');
47
+ }
48
+
49
+ logger.log('INFO', '[Dispatcher] ✅ Obtained OAuth 2.0 access token for callback');
50
+
51
+ // Add timeout to prevent hanging (30 seconds should be plenty for a callback)
52
+ const timeoutPromise = new Promise((_, reject) =>
53
+ setTimeout(() => reject(new Error('Callback timeout after 30s')), 30000)
54
+ );
55
+
56
+ const fetchPromise = directFetch(callbackUrl, {
57
+ method: 'POST',
58
+ headers: {
59
+ 'Content-Type': 'application/json',
60
+ 'Authorization': `Bearer ${accessToken}`
61
+ },
62
+ body: JSON.stringify(payload)
63
+ });
64
+
65
+ const response = await Promise.race([fetchPromise, timeoutPromise]);
66
+
67
+ if (!response.ok) {
68
+ const errorText = await response.text().catch(() => 'Unable to read response');
69
+ throw new Error(`Callback returned non-OK status: ${response.status} ${response.statusText}. Response: ${errorText.substring(0, 200)}`);
70
+ }
71
+
72
+ logger.log('INFO', `[Dispatcher] ✅ Callback sent successfully (status: ${response.status})`);
73
+ return response;
74
+ } catch (err) {
75
+ logger.log('ERROR', `[Dispatcher] Authenticated callback failed: ${err.message}. Stack: ${err.stack?.substring(0, 300)}`);
76
+ throw err;
77
+ }
78
+ }
79
+
22
80
  // ... [SHARED UTILS and SHARED ASYNC HELPERS remain exactly the same] ...
23
81
 
24
82
  function getMillis(field) {
@@ -168,27 +226,9 @@ async function handleSnapshot(config, dependencies, reqBody) {
168
226
  if (callbackUrl) {
169
227
  logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow at: ${callbackUrl}`);
170
228
  try {
171
- // Add timeout to prevent hanging (30 seconds should be plenty for a callback)
172
- const timeoutPromise = new Promise((_, reject) =>
173
- setTimeout(() => reject(new Error('Callback timeout after 30s')), 30000)
174
- );
175
-
176
- const fetchPromise = directFetch(callbackUrl, {
177
- method: 'POST',
178
- headers: { 'Content-Type': 'application/json' },
179
- body: JSON.stringify(finalResult)
180
- });
181
-
182
- const response = await Promise.race([fetchPromise, timeoutPromise]);
183
-
184
- if (!response.ok) {
185
- const errorText = await response.text().catch(() => 'Unable to read response');
186
- logger.log('ERROR', `[Dispatcher] Callback returned non-OK status: ${response.status} ${response.statusText}. Response: ${errorText.substring(0, 200)}`);
187
- } else {
188
- logger.log('INFO', `[Dispatcher] ✅ Callback sent successfully (status: ${response.status})`);
189
- }
229
+ await sendAuthenticatedCallback(callbackUrl, finalResult, logger, directFetch);
190
230
  } catch (err) {
191
- logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}. Stack: ${err.stack?.substring(0, 300)}`);
231
+ logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}`);
192
232
  }
193
233
  } else {
194
234
  logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified');
@@ -240,27 +280,9 @@ async function handleSnapshot(config, dependencies, reqBody) {
240
280
  if (callbackUrl) {
241
281
  logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow at: ${callbackUrl}`);
242
282
  try {
243
- // Add timeout to prevent hanging (30 seconds should be plenty for a callback)
244
- const timeoutPromise = new Promise((_, reject) =>
245
- setTimeout(() => reject(new Error('Callback timeout after 30s')), 30000)
246
- );
247
-
248
- const fetchPromise = directFetch(callbackUrl, {
249
- method: 'POST',
250
- headers: { 'Content-Type': 'application/json' },
251
- body: JSON.stringify(finalResult)
252
- });
253
-
254
- const response = await Promise.race([fetchPromise, timeoutPromise]);
255
-
256
- if (!response.ok) {
257
- const errorText = await response.text().catch(() => 'Unable to read response');
258
- logger.log('ERROR', `[Dispatcher] Callback returned non-OK status: ${response.status} ${response.statusText}. Response: ${errorText.substring(0, 200)}`);
259
- } else {
260
- logger.log('INFO', `[Dispatcher] ✅ Callback sent successfully (status: ${response.status})`);
261
- }
283
+ await sendAuthenticatedCallback(callbackUrl, finalResult, logger, directFetch);
262
284
  } catch (err) {
263
- logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}. Stack: ${err.stack?.substring(0, 300)}`);
285
+ logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}`);
264
286
  }
265
287
  } else {
266
288
  logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified');
@@ -275,27 +297,9 @@ async function handleSnapshot(config, dependencies, reqBody) {
275
297
  if (callbackUrl) {
276
298
  logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow with error at: ${callbackUrl}`);
277
299
  try {
278
- // Add timeout to prevent hanging (30 seconds should be plenty for a callback)
279
- const timeoutPromise = new Promise((_, reject) =>
280
- setTimeout(() => reject(new Error('Callback timeout after 30s')), 30000)
281
- );
282
-
283
- const fetchPromise = directFetch(callbackUrl, {
284
- method: 'POST',
285
- headers: { 'Content-Type': 'application/json' },
286
- body: JSON.stringify(errorResult)
287
- });
288
-
289
- const response = await Promise.race([fetchPromise, timeoutPromise]);
290
-
291
- if (!response.ok) {
292
- const errorText = await response.text().catch(() => 'Unable to read response');
293
- logger.log('ERROR', `[Dispatcher] Error callback returned non-OK status: ${response.status} ${response.statusText}. Response: ${errorText.substring(0, 200)}`);
294
- } else {
295
- logger.log('INFO', `[Dispatcher] ✅ Error callback sent successfully (status: ${response.status})`);
296
- }
300
+ await sendAuthenticatedCallback(callbackUrl, errorResult, logger, directFetch);
297
301
  } catch (err) {
298
- logger.log('ERROR', `[Dispatcher] Error callback failed: ${err.message}. Stack: ${err.stack?.substring(0, 300)}`);
302
+ logger.log('ERROR', `[Dispatcher] Error callback failed: ${err.message}`);
299
303
  }
300
304
  } else {
301
305
  logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified of error');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.667",
3
+ "version": "1.0.668",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [