bulltrackers-module 1.0.666 → 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) {
@@ -136,6 +194,8 @@ async function handleSnapshot(config, dependencies, reqBody) {
136
194
  const targetDate = reqBody.date; // Optional: if provided, only process up to this date
137
195
  const callbackUrl = reqBody.callback_url; // Callback URL from workflow
138
196
 
197
+ logger.log('INFO', `[Dispatcher] 📸 Snapshot request received. Date: ${targetDate || 'all'}, Callback URL: ${callbackUrl || 'NOT PROVIDED'}`);
198
+
139
199
  // Use native fetch if available (Node 18+), otherwise fall back to node-fetch
140
200
  const directFetch = typeof fetch !== 'undefined' ? fetch : require('node-fetch');
141
201
 
@@ -164,12 +224,14 @@ async function handleSnapshot(config, dependencies, reqBody) {
164
224
 
165
225
  // Send callback if provided
166
226
  if (callbackUrl) {
167
- logger.log('INFO', '[Dispatcher] 📞 Calling back Workflow...');
168
- await directFetch(callbackUrl, {
169
- method: 'POST',
170
- headers: { 'Content-Type': 'application/json' },
171
- body: JSON.stringify(finalResult)
172
- }).catch(err => logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}`));
227
+ logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow at: ${callbackUrl}`);
228
+ try {
229
+ await sendAuthenticatedCallback(callbackUrl, finalResult, logger, directFetch);
230
+ } catch (err) {
231
+ logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}`);
232
+ }
233
+ } else {
234
+ logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified');
173
235
  }
174
236
 
175
237
  return finalResult;
@@ -216,12 +278,14 @@ async function handleSnapshot(config, dependencies, reqBody) {
216
278
 
217
279
  // Send callback to workflow if provided
218
280
  if (callbackUrl) {
219
- logger.log('INFO', '[Dispatcher] 📞 Calling back Workflow...');
220
- await directFetch(callbackUrl, {
221
- method: 'POST',
222
- headers: { 'Content-Type': 'application/json' },
223
- body: JSON.stringify(finalResult)
224
- }).catch(err => logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}`));
281
+ logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow at: ${callbackUrl}`);
282
+ try {
283
+ await sendAuthenticatedCallback(callbackUrl, finalResult, logger, directFetch);
284
+ } catch (err) {
285
+ logger.log('ERROR', `[Dispatcher] Callback failed: ${err.message}`);
286
+ }
287
+ } else {
288
+ logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified');
225
289
  }
226
290
 
227
291
  return finalResult;
@@ -231,12 +295,14 @@ async function handleSnapshot(config, dependencies, reqBody) {
231
295
 
232
296
  // Notify workflow of failure too, otherwise it hangs!
233
297
  if (callbackUrl) {
234
- logger.log('INFO', '[Dispatcher] 📞 Calling back Workflow with error...');
235
- await directFetch(callbackUrl, {
236
- method: 'POST',
237
- headers: { 'Content-Type': 'application/json' },
238
- body: JSON.stringify(errorResult)
239
- }).catch(err => logger.log('ERROR', `[Dispatcher] Error callback failed: ${err.message}`));
298
+ logger.log('INFO', `[Dispatcher] 📞 Calling back Workflow with error at: ${callbackUrl}`);
299
+ try {
300
+ await sendAuthenticatedCallback(callbackUrl, errorResult, logger, directFetch);
301
+ } catch (err) {
302
+ logger.log('ERROR', `[Dispatcher] Error callback failed: ${err.message}`);
303
+ }
304
+ } else {
305
+ logger.log('WARN', '[Dispatcher] No callback URL provided, workflow will not be notified of error');
240
306
  }
241
307
 
242
308
  return errorResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.666",
3
+ "version": "1.0.668",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [