bulltrackers-module 1.0.603 → 1.0.605

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.
@@ -1835,13 +1835,81 @@ const getSyncStatus = async (db, targetId) => {
1835
1835
 
1836
1836
  if (snap.empty) return { status: 'never_requested' };
1837
1837
 
1838
- const data = snap.docs[0].data();
1839
- return {
1840
- requestId: data.requestId,
1841
- status: data.status, // queued, dispatched, processing, completed, failed
1842
- updatedAt: data.updatedAt,
1838
+ const doc = snap.docs[0];
1839
+ const data = doc.data();
1840
+ const requestId = data.requestId;
1841
+ const currentStatus = data.status;
1842
+
1843
+ // Server-side timeout: Check if request is stale (older than 2 minutes and still processing)
1844
+ const STALE_THRESHOLD_MS = 2 * 60 * 1000; // 2 minutes
1845
+ const processingStates = ['queued', 'dispatched', 'processing', 'indexing', 'computing'];
1846
+ const isProcessingState = processingStates.includes(currentStatus);
1847
+
1848
+ if (isProcessingState) {
1849
+ // Check age of request
1850
+ const createdAt = data.createdAt;
1851
+ const updatedAt = data.updatedAt;
1852
+
1853
+ // Use updatedAt if available, otherwise createdAt
1854
+ const checkTime = updatedAt || createdAt;
1855
+ if (checkTime) {
1856
+ const checkTimestamp = checkTime.toMillis ? checkTime.toMillis() : (checkTime.getTime ? checkTime.getTime() : new Date(checkTime).getTime());
1857
+ const age = Date.now() - checkTimestamp;
1858
+
1859
+ if (age > STALE_THRESHOLD_MS) {
1860
+ // Mark as failed due to timeout
1861
+ const errorMessage = `Sync request timed out after ${Math.round(age / 1000 / 60)} minutes. The task may have failed.`;
1862
+ await doc.ref.update({
1863
+ status: 'failed',
1864
+ error: errorMessage,
1865
+ failedAt: new Date(),
1866
+ updatedAt: new Date()
1867
+ });
1868
+
1869
+ return {
1870
+ requestId: requestId,
1871
+ status: 'failed',
1872
+ error: errorMessage,
1873
+ updatedAt: new Date(),
1874
+ type: data.userType,
1875
+ createdAt: createdAt,
1876
+ failedAt: new Date()
1877
+ };
1878
+ }
1879
+ }
1880
+ }
1881
+
1882
+ // Return current status with timestamps
1883
+ const result = {
1884
+ requestId: requestId,
1885
+ status: currentStatus,
1843
1886
  type: data.userType
1844
1887
  };
1888
+
1889
+ // Add timestamps if available
1890
+ if (data.createdAt) {
1891
+ result.createdAt = data.createdAt.toDate ? data.createdAt.toDate().toISOString() : (data.createdAt.toISOString ? data.createdAt.toISOString() : data.createdAt);
1892
+ }
1893
+ if (data.updatedAt) {
1894
+ result.updatedAt = data.updatedAt.toDate ? data.updatedAt.toDate().toISOString() : (data.updatedAt.toISOString ? data.updatedAt.toISOString() : data.updatedAt);
1895
+ }
1896
+ if (data.dispatchedAt) {
1897
+ result.dispatchedAt = data.dispatchedAt.toDate ? data.dispatchedAt.toDate().toISOString() : (data.dispatchedAt.toISOString ? data.dispatchedAt.toISOString() : data.dispatchedAt);
1898
+ }
1899
+ if (data.completedAt) {
1900
+ result.completedAt = data.completedAt.toDate ? data.completedAt.toDate().toISOString() : (data.completedAt.toISOString ? data.completedAt.toISOString() : data.completedAt);
1901
+ }
1902
+ if (data.failedAt) {
1903
+ result.failedAt = data.failedAt.toDate ? data.failedAt.toDate().toISOString() : (data.failedAt.toISOString ? data.failedAt.toISOString() : data.failedAt);
1904
+ }
1905
+ if (data.error) {
1906
+ result.error = data.error;
1907
+ }
1908
+
1909
+ // Add canRequest flag (true if not in processing state or failed)
1910
+ result.canRequest = !isProcessingState && currentStatus !== 'failed';
1911
+
1912
+ return result;
1845
1913
  };
1846
1914
 
1847
1915
  // ==========================================
@@ -1935,39 +2003,51 @@ const checkDataStatus = async (db, userId) => {
1935
2003
  let computationDate = null;
1936
2004
  let fallbackWindowExhausted = false;
1937
2005
 
1938
- // Determine which computation to check based on user type
1939
- let computationName = 'SignedInUserProfileMetrics';
2006
+ // Determine which computations to check based on user type
2007
+ // Signed-in users always have SignedInUserProfileMetrics
2008
+ // If they're also a PI, they may have SignedInUserPIPersonalizedMetrics
2009
+ const computationNames = ['SignedInUserProfileMetrics'];
1940
2010
  try {
1941
2011
  await fetchPopularInvestorMasterList(db, userId);
1942
- computationName = 'SignedInUserPIPersonalizedMetrics';
2012
+ // User is a PI, also check for PI-specific computation
2013
+ computationNames.push('SignedInUserPIPersonalizedMetrics');
1943
2014
  } catch (e) {
1944
- // User is not a PI, use default
2015
+ // User is not a PI, only check SignedInUserProfileMetrics
1945
2016
  }
1946
2017
 
1947
2018
  // Check for computation results in the last 7 days
2019
+ // Check all relevant computation names for this user
1948
2020
  for (let i = 0; i < lookbackDays; i++) {
1949
2021
  const checkDate = new Date(today);
1950
2022
  checkDate.setDate(checkDate.getDate() - i);
1951
2023
  const dateStr = checkDate.toISOString().split('T')[0];
1952
2024
 
1953
- try {
1954
- const pageRef = db.collection('unified_insights')
1955
- .doc(dateStr)
1956
- .collection('results')
1957
- .doc('popular-investor')
1958
- .collection('computations')
1959
- .doc(computationName)
1960
- .collection('pages')
1961
- .doc(String(userId));
1962
-
1963
- const pageSnap = await pageRef.get();
1964
- if (pageSnap.exists) {
1965
- computationDate = dateStr;
1966
- break;
2025
+ // Check each computation name
2026
+ for (const compName of computationNames) {
2027
+ try {
2028
+ const pageRef = db.collection('unified_insights')
2029
+ .doc(dateStr)
2030
+ .collection('results')
2031
+ .doc('popular-investor')
2032
+ .collection('computations')
2033
+ .doc(compName)
2034
+ .collection('pages')
2035
+ .doc(String(userId));
2036
+
2037
+ const pageSnap = await pageRef.get();
2038
+ if (pageSnap.exists) {
2039
+ computationDate = dateStr;
2040
+ break;
2041
+ }
2042
+ } catch (error) {
2043
+ // Continue checking other dates/computations
2044
+ console.error(`Error checking computation ${compName} for ${dateStr}:`, error);
1967
2045
  }
1968
- } catch (error) {
1969
- // Continue checking other dates
1970
- console.error(`Error checking computation for ${dateStr}:`, error);
2046
+ }
2047
+
2048
+ // If we found a computation date, stop checking
2049
+ if (computationDate) {
2050
+ break;
1971
2051
  }
1972
2052
  }
1973
2053
 
@@ -244,7 +244,12 @@ async function handleComputationTask(message, config, dependencies) {
244
244
  computation,
245
245
  getComputationDisplayName(computation),
246
246
  true,
247
- null
247
+ null,
248
+ {
249
+ collectionRegistry: dependencies.collectionRegistry,
250
+ config: config,
251
+ notificationType: 'userActionCompletions'
252
+ }
248
253
  );
249
254
  } catch (notifError) {
250
255
  // Non-critical, log and continue
@@ -291,7 +296,12 @@ async function handleComputationTask(message, config, dependencies) {
291
296
  computation,
292
297
  getComputationDisplayName(computation),
293
298
  false,
294
- err.message
299
+ err.message,
300
+ {
301
+ collectionRegistry: dependencies.collectionRegistry,
302
+ config: config,
303
+ notificationType: 'userActionCompletions'
304
+ }
295
305
  );
296
306
  } catch (notifError) {
297
307
  // Non-critical, log and continue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.603",
3
+ "version": "1.0.605",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [