bulltrackers-module 1.0.452 → 1.0.454
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.
|
@@ -234,6 +234,47 @@ async function getUserSyncStatus(req, res, dependencies, config) {
|
|
|
234
234
|
const latestRequest = requestsSnapshot.docs[0].data();
|
|
235
235
|
let status = latestRequest.status || 'queued';
|
|
236
236
|
|
|
237
|
+
// Check if request is stale (stuck in processing state for too long)
|
|
238
|
+
const STALE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
|
|
239
|
+
const processingStates = ['processing', 'dispatched', 'indexing', 'computing', 'queued'];
|
|
240
|
+
const isProcessingState = processingStates.includes(status);
|
|
241
|
+
|
|
242
|
+
let isStale = false;
|
|
243
|
+
if (isProcessingState) {
|
|
244
|
+
const now = Date.now();
|
|
245
|
+
const createdAt = latestRequest.createdAt?.toDate?.()?.getTime() ||
|
|
246
|
+
latestRequest.createdAt?.toMillis?.() || null;
|
|
247
|
+
const dispatchedAt = latestRequest.dispatchedAt?.toDate?.()?.getTime() ||
|
|
248
|
+
latestRequest.dispatchedAt?.toMillis?.() || null;
|
|
249
|
+
const updatedAt = latestRequest.updatedAt?.toDate?.()?.getTime() ||
|
|
250
|
+
latestRequest.updatedAt?.toMillis?.() || null;
|
|
251
|
+
|
|
252
|
+
// Use the most recent timestamp to determine age
|
|
253
|
+
const referenceTime = dispatchedAt || createdAt || updatedAt;
|
|
254
|
+
|
|
255
|
+
if (referenceTime && (now - referenceTime) > STALE_THRESHOLD_MS) {
|
|
256
|
+
isStale = true;
|
|
257
|
+
logger.log('WARN', `[getUserSyncStatus] Detected stale request ${latestRequest.requestId} for user ${targetCidNum}. Status: ${status}, Age: ${Math.round((now - referenceTime) / 60000)} minutes`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// If stale, mark as failed to stop polling
|
|
262
|
+
if (isStale) {
|
|
263
|
+
status = 'failed';
|
|
264
|
+
const requestDocRef = requestsSnapshot.docs[0].ref;
|
|
265
|
+
try {
|
|
266
|
+
await requestDocRef.update({
|
|
267
|
+
status: 'failed',
|
|
268
|
+
error: 'Request timed out - task may have failed to process. Please try again.',
|
|
269
|
+
failedAt: FieldValue.serverTimestamp(),
|
|
270
|
+
updatedAt: FieldValue.serverTimestamp()
|
|
271
|
+
});
|
|
272
|
+
logger.log('INFO', `[getUserSyncStatus] Marked stale request ${latestRequest.requestId} as failed`);
|
|
273
|
+
} catch (updateErr) {
|
|
274
|
+
logger.log('WARN', `[getUserSyncStatus] Failed to update stale request status`, updateErr);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
237
278
|
const response = {
|
|
238
279
|
success: true,
|
|
239
280
|
status,
|
|
@@ -249,8 +290,11 @@ async function getUserSyncStatus(req, res, dependencies, config) {
|
|
|
249
290
|
|
|
250
291
|
// Include error details if status is failed
|
|
251
292
|
if (status === 'failed') {
|
|
252
|
-
response.error =
|
|
253
|
-
|
|
293
|
+
response.error = isStale
|
|
294
|
+
? 'Request timed out - task may have failed to process. Please try again.'
|
|
295
|
+
: (latestRequest.error || 'Unknown error occurred');
|
|
296
|
+
response.failedAt = latestRequest.failedAt?.toDate?.()?.toISOString() ||
|
|
297
|
+
(isStale ? new Date().toISOString() : null);
|
|
254
298
|
}
|
|
255
299
|
|
|
256
300
|
// Include raw data status if processing
|
|
@@ -474,10 +474,19 @@ async function handlePopularInvestorUpdate(taskData, config, dependencies) {
|
|
|
474
474
|
*/
|
|
475
475
|
async function handleOnDemandUserUpdate(taskData, config, dependencies) {
|
|
476
476
|
const { cid, username, requestId, source } = taskData;
|
|
477
|
-
const { ETORO_API_PORTFOLIO_URL, ETORO_API_HISTORY_URL } = config;
|
|
478
477
|
|
|
479
|
-
// [FIX] Destructure
|
|
478
|
+
// [FIX] Destructure dependencies first
|
|
480
479
|
const { logger, proxyManager, batchManager, headerManager, db } = dependencies;
|
|
480
|
+
|
|
481
|
+
// Validate and set API URLs with defaults and fallbacks
|
|
482
|
+
const ETORO_API_PORTFOLIO_URL = config.ETORO_API_PORTFOLIO_URL || process.env.ETORO_API_PORTFOLIO_URL || 'https://www.etoro.com/sapi/portfolios/portfolio';
|
|
483
|
+
const ETORO_API_HISTORY_URL = config.ETORO_API_HISTORY_URL || process.env.ETORO_API_HISTORY_URL || 'https://www.etoro.com/sapi/trade-data-real/history/public/credit/flat';
|
|
484
|
+
|
|
485
|
+
if (!ETORO_API_PORTFOLIO_URL || ETORO_API_PORTFOLIO_URL === 'undefined') {
|
|
486
|
+
const errorMsg = 'ETORO_API_PORTFOLIO_URL is not configured. Check taskEngine config.';
|
|
487
|
+
logger.log('ERROR', `[On-Demand Update] ${errorMsg}`);
|
|
488
|
+
throw new Error(errorMsg);
|
|
489
|
+
}
|
|
481
490
|
|
|
482
491
|
logger.log('INFO', `[On-Demand Update] Fetching Portfolio & History for Signed-In User: ${username} (${cid})${requestId ? ` [Request: ${requestId}]` : ''}`);
|
|
483
492
|
|
|
@@ -636,8 +645,49 @@ async function handleOnDemandUserUpdate(taskData, config, dependencies) {
|
|
|
636
645
|
}
|
|
637
646
|
|
|
638
647
|
logger.log('SUCCESS', `[On-Demand Update] Complete for ${username}`);
|
|
648
|
+
|
|
649
|
+
// Update request status to completed if this is a sync request
|
|
650
|
+
if (requestId && source === 'on_demand_sync' && db) {
|
|
651
|
+
try {
|
|
652
|
+
const requestRef = db.collection('user_sync_requests')
|
|
653
|
+
.doc(String(cid))
|
|
654
|
+
.collection('requests')
|
|
655
|
+
.doc(requestId);
|
|
656
|
+
|
|
657
|
+
await requestRef.update({
|
|
658
|
+
status: 'completed',
|
|
659
|
+
completedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
|
|
660
|
+
updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
|
|
661
|
+
});
|
|
662
|
+
logger.log('INFO', `[On-Demand Update] Updated sync request ${requestId} to completed`);
|
|
663
|
+
} catch (err) {
|
|
664
|
+
logger.log('WARN', `[On-Demand Update] Failed to update request status to completed for ${requestId}`, err);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
639
667
|
} catch (error) {
|
|
640
668
|
logger.log('ERROR', `[On-Demand Update] Failed for ${username}`, error);
|
|
669
|
+
|
|
670
|
+
// Update request status to failed if this is a sync request
|
|
671
|
+
if (requestId && source === 'on_demand_sync' && db) {
|
|
672
|
+
try {
|
|
673
|
+
const requestRef = db.collection('user_sync_requests')
|
|
674
|
+
.doc(String(cid))
|
|
675
|
+
.collection('requests')
|
|
676
|
+
.doc(requestId);
|
|
677
|
+
|
|
678
|
+
const errorMessage = error.message || 'Unknown error occurred';
|
|
679
|
+
await requestRef.update({
|
|
680
|
+
status: 'failed',
|
|
681
|
+
error: errorMessage,
|
|
682
|
+
failedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp(),
|
|
683
|
+
updatedAt: require('@google-cloud/firestore').FieldValue.serverTimestamp()
|
|
684
|
+
});
|
|
685
|
+
logger.log('INFO', `[On-Demand Update] Updated sync request ${requestId} to failed: ${errorMessage}`);
|
|
686
|
+
} catch (err) {
|
|
687
|
+
logger.log('WARN', `[On-Demand Update] Failed to update request status to failed for ${requestId}`, err);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
641
691
|
throw error;
|
|
642
692
|
} finally {
|
|
643
693
|
if (headerManager && headerId !== 'fallback') {
|