bulltrackers-module 1.0.423 → 1.0.424
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.
|
@@ -567,20 +567,30 @@ async function getUserPortfolio(req, res, dependencies, config) {
|
|
|
567
567
|
}
|
|
568
568
|
|
|
569
569
|
try {
|
|
570
|
+
// Check for dev override impersonation
|
|
571
|
+
const { getEffectiveCid, getDevOverride } = require('./dev_helpers');
|
|
572
|
+
const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
|
|
573
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
574
|
+
const isImpersonating = devOverride && devOverride.impersonateCid && effectiveCid !== Number(userCid);
|
|
575
|
+
|
|
570
576
|
const { signedInUsersCollection } = config;
|
|
571
577
|
const CANARY_BLOCK_ID = '19M';
|
|
572
578
|
const today = new Date().toISOString().split('T')[0];
|
|
573
579
|
|
|
574
|
-
//
|
|
575
|
-
const dataDate = await findLatestPortfolioDate(db, signedInUsersCollection,
|
|
580
|
+
// Use effective CID for portfolio lookup
|
|
581
|
+
const dataDate = await findLatestPortfolioDate(db, signedInUsersCollection, effectiveCid, 30);
|
|
576
582
|
|
|
577
583
|
if (!dataDate) {
|
|
578
|
-
return res.status(404).json({
|
|
584
|
+
return res.status(404).json({
|
|
585
|
+
error: "Portfolio data not found for this user (checked last 30 days)",
|
|
586
|
+
effectiveCid: effectiveCid,
|
|
587
|
+
isImpersonating: isImpersonating || false
|
|
588
|
+
});
|
|
579
589
|
}
|
|
580
590
|
|
|
581
591
|
const isFallback = dataDate !== today;
|
|
582
592
|
if (isFallback) {
|
|
583
|
-
logger.log('INFO', `[getUserPortfolio] Using fallback date ${dataDate} for
|
|
593
|
+
logger.log('INFO', `[getUserPortfolio] Using fallback date ${dataDate} for effective CID ${effectiveCid} (today: ${today})`);
|
|
584
594
|
}
|
|
585
595
|
|
|
586
596
|
// Fetch portfolio from signed_in_users/19M/snapshots/{date}/parts/part_X
|
|
@@ -594,11 +604,11 @@ async function getUserPortfolio(req, res, dependencies, config) {
|
|
|
594
604
|
|
|
595
605
|
let portfolioData = null;
|
|
596
606
|
|
|
597
|
-
// Search through all parts to find the user's portfolio
|
|
607
|
+
// Search through all parts to find the user's portfolio (use effective CID)
|
|
598
608
|
for (const partDoc of partsSnapshot.docs) {
|
|
599
609
|
const partData = partDoc.data();
|
|
600
|
-
if (partData && partData[String(
|
|
601
|
-
portfolioData = partData[String(
|
|
610
|
+
if (partData && partData[String(effectiveCid)]) {
|
|
611
|
+
portfolioData = partData[String(effectiveCid)];
|
|
602
612
|
break;
|
|
603
613
|
}
|
|
604
614
|
}
|
|
@@ -773,6 +783,12 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
773
783
|
}
|
|
774
784
|
|
|
775
785
|
try {
|
|
786
|
+
// Check for dev override impersonation
|
|
787
|
+
const { getEffectiveCid, getDevOverride } = require('./dev_helpers');
|
|
788
|
+
const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
|
|
789
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
790
|
+
const isImpersonating = devOverride && devOverride.impersonateCid && effectiveCid !== Number(userCid);
|
|
791
|
+
|
|
776
792
|
const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
|
|
777
793
|
const resultsSub = config.resultsSubcollection || 'results';
|
|
778
794
|
const compsSub = config.computationsSubcollection || 'computations';
|
|
@@ -789,14 +805,13 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
789
805
|
return res.status(400).json({ error: "Please specify at least one computation name" });
|
|
790
806
|
}
|
|
791
807
|
|
|
792
|
-
// Check for dev override (for developer accounts)
|
|
793
|
-
const { getDevOverride } = require('./dev_helpers');
|
|
794
|
-
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
808
|
+
// Check for dev override (for developer accounts) - use actual userCid for override check
|
|
795
809
|
const isDevOverrideActive = devOverride && devOverride.enabled && devOverride.fakeCopiedPIs.length > 0;
|
|
796
810
|
|
|
797
811
|
let datesToCheck = [today];
|
|
798
812
|
|
|
799
813
|
// If mode is 'latest', try to find the latest available date for the first computation
|
|
814
|
+
// Use effectiveCid for computation lookup
|
|
800
815
|
if (mode === 'latest') {
|
|
801
816
|
const firstCompName = computationNames[0];
|
|
802
817
|
const latestDate = await findLatestComputationDate(
|
|
@@ -806,25 +821,27 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
806
821
|
compsSub,
|
|
807
822
|
category,
|
|
808
823
|
firstCompName,
|
|
809
|
-
|
|
824
|
+
effectiveCid,
|
|
810
825
|
30
|
|
811
826
|
);
|
|
812
827
|
|
|
813
828
|
if (latestDate) {
|
|
814
829
|
datesToCheck = [latestDate];
|
|
815
830
|
if (latestDate !== today) {
|
|
816
|
-
logger.log('INFO', `[getUserComputations] Using fallback date ${latestDate} for
|
|
831
|
+
logger.log('INFO', `[getUserComputations] Using fallback date ${latestDate} for effective CID ${effectiveCid} (today: ${today})`);
|
|
817
832
|
}
|
|
818
833
|
} else {
|
|
819
834
|
// No data found, return empty
|
|
820
835
|
return res.status(200).json({
|
|
821
836
|
status: 'success',
|
|
822
|
-
userCid: String(
|
|
837
|
+
userCid: String(effectiveCid),
|
|
823
838
|
mode,
|
|
824
839
|
computations: computationNames,
|
|
825
840
|
data: {},
|
|
826
841
|
isFallback: false,
|
|
827
|
-
requestedDate: today
|
|
842
|
+
requestedDate: today,
|
|
843
|
+
isImpersonating: isImpersonating || false,
|
|
844
|
+
actualCid: Number(userCid)
|
|
828
845
|
});
|
|
829
846
|
}
|
|
830
847
|
} else if (mode === 'series') {
|
|
@@ -861,9 +878,10 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
861
878
|
const rawData = doc.data();
|
|
862
879
|
const data = tryDecompress(rawData);
|
|
863
880
|
// Filter by user CID - computation results are stored as { cid: result }
|
|
864
|
-
|
|
881
|
+
// Use effectiveCid for lookup
|
|
882
|
+
let userResult = data[String(effectiveCid)];
|
|
865
883
|
|
|
866
|
-
// Apply dev override for computations that include copied PIs
|
|
884
|
+
// Apply dev override for computations that include copied PIs (use actual userCid for override check)
|
|
867
885
|
if (isDevOverrideActive && (compName === 'SignedInUserProfileMetrics' || compName === 'SignedInUserCopiedPIs')) {
|
|
868
886
|
if (compName === 'SignedInUserCopiedPIs') {
|
|
869
887
|
// Override the copied PIs list
|
|
@@ -923,13 +941,15 @@ async function getUserComputations(req, res, dependencies, config) {
|
|
|
923
941
|
|
|
924
942
|
return res.status(200).json({
|
|
925
943
|
status: 'success',
|
|
926
|
-
userCid: String(
|
|
944
|
+
userCid: String(effectiveCid),
|
|
927
945
|
mode,
|
|
928
946
|
computations: computationNames,
|
|
929
947
|
data: cleanedResults,
|
|
930
948
|
isFallback: isFallback,
|
|
931
949
|
requestedDate: today,
|
|
932
|
-
devOverrideActive: isDevOverrideActive
|
|
950
|
+
devOverrideActive: isDevOverrideActive,
|
|
951
|
+
isImpersonating: isImpersonating || false,
|
|
952
|
+
actualCid: Number(userCid)
|
|
933
953
|
});
|
|
934
954
|
|
|
935
955
|
} catch (error) {
|
|
@@ -1001,7 +1021,8 @@ async function autoGenerateWatchlist(req, res, dependencies, config) {
|
|
|
1001
1021
|
|
|
1002
1022
|
if (computationDoc.exists) {
|
|
1003
1023
|
const computationData = computationDoc.data();
|
|
1004
|
-
|
|
1024
|
+
// Use effectiveCid for lookup
|
|
1025
|
+
const userResult = computationData[String(effectiveCid)];
|
|
1005
1026
|
|
|
1006
1027
|
if (userResult && userResult.current && userResult.current.length > 0) {
|
|
1007
1028
|
// Convert computation result to our format
|
|
@@ -1742,6 +1763,7 @@ async function getPiProfile(req, res, dependencies, config) {
|
|
|
1742
1763
|
/**
|
|
1743
1764
|
* GET /user/me/is-popular-investor
|
|
1744
1765
|
* Check if signed-in user is also a Popular Investor
|
|
1766
|
+
* Supports dev override impersonation
|
|
1745
1767
|
*/
|
|
1746
1768
|
async function checkIfUserIsPopularInvestor(req, res, dependencies, config) {
|
|
1747
1769
|
const { db, logger } = dependencies;
|
|
@@ -1752,18 +1774,25 @@ async function checkIfUserIsPopularInvestor(req, res, dependencies, config) {
|
|
|
1752
1774
|
}
|
|
1753
1775
|
|
|
1754
1776
|
try {
|
|
1755
|
-
|
|
1777
|
+
// Check for dev override impersonation
|
|
1778
|
+
const { getEffectiveCid, getDevOverride } = require('./dev_helpers');
|
|
1779
|
+
const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
|
|
1780
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
1781
|
+
const isImpersonating = devOverride && devOverride.impersonateCid && effectiveCid !== Number(userCid);
|
|
1782
|
+
|
|
1783
|
+
// Use effective CID (impersonated or actual) to check PI status
|
|
1784
|
+
const rankEntry = await checkIfUserIsPI(db, effectiveCid, config, logger);
|
|
1756
1785
|
|
|
1757
1786
|
if (!rankEntry) {
|
|
1758
1787
|
return res.status(200).json({
|
|
1759
1788
|
isPopularInvestor: false,
|
|
1760
|
-
rankingData: null
|
|
1789
|
+
rankingData: null,
|
|
1790
|
+
isImpersonating: isImpersonating || false,
|
|
1791
|
+
effectiveCid: effectiveCid
|
|
1761
1792
|
});
|
|
1762
1793
|
}
|
|
1763
1794
|
|
|
1764
|
-
// Check if this is a dev override
|
|
1765
|
-
const { getDevOverride } = require('./dev_helpers');
|
|
1766
|
-
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
1795
|
+
// Check if this is a dev override (pretendToBePI)
|
|
1767
1796
|
const isDevOverride = devOverride && devOverride.pretendToBePI;
|
|
1768
1797
|
|
|
1769
1798
|
// Return ranking data
|
|
@@ -1779,7 +1808,10 @@ async function checkIfUserIsPopularInvestor(req, res, dependencies, config) {
|
|
|
1779
1808
|
winRatio: rankEntry.WinRatio || 0,
|
|
1780
1809
|
trades: rankEntry.Trades || 0
|
|
1781
1810
|
},
|
|
1782
|
-
isDevOverride: isDevOverride || false
|
|
1811
|
+
isDevOverride: isDevOverride || false,
|
|
1812
|
+
isImpersonating: isImpersonating || false,
|
|
1813
|
+
effectiveCid: effectiveCid,
|
|
1814
|
+
actualCid: Number(userCid)
|
|
1783
1815
|
});
|
|
1784
1816
|
|
|
1785
1817
|
} catch (error) {
|
|
@@ -2084,34 +2116,42 @@ async function getSignedInUserPIPersonalizedMetrics(req, res, dependencies, conf
|
|
|
2084
2116
|
}
|
|
2085
2117
|
|
|
2086
2118
|
try {
|
|
2087
|
-
//
|
|
2088
|
-
const
|
|
2119
|
+
// Check for dev override impersonation
|
|
2120
|
+
const { getEffectiveCid, getDevOverride } = require('./dev_helpers');
|
|
2121
|
+
const effectiveCid = await getEffectiveCid(db, userCid, config, logger);
|
|
2122
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
2123
|
+
const isImpersonating = devOverride && devOverride.impersonateCid && effectiveCid !== Number(userCid);
|
|
2124
|
+
|
|
2125
|
+
// Use effective CID (impersonated or actual) to check PI status
|
|
2126
|
+
const rankEntry = await checkIfUserIsPI(db, effectiveCid, config, logger);
|
|
2089
2127
|
if (!rankEntry) {
|
|
2090
2128
|
return res.status(404).json({
|
|
2091
2129
|
error: "Not a Popular Investor",
|
|
2092
|
-
message: "This endpoint is only available for users who are Popular Investors"
|
|
2130
|
+
message: "This endpoint is only available for users who are Popular Investors",
|
|
2131
|
+
effectiveCid: effectiveCid,
|
|
2132
|
+
isImpersonating: isImpersonating || false
|
|
2093
2133
|
});
|
|
2094
2134
|
}
|
|
2095
2135
|
|
|
2096
|
-
// Check if this is a dev override
|
|
2097
|
-
const { getDevOverride } = require('./dev_helpers');
|
|
2098
|
-
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
2136
|
+
// Check if this is a dev override (pretendToBePI)
|
|
2099
2137
|
const isDevOverride = devOverride && devOverride.pretendToBePI;
|
|
2100
2138
|
|
|
2101
|
-
// If dev override, generate sample data
|
|
2139
|
+
// If dev override (pretendToBePI), generate sample data
|
|
2102
2140
|
if (isDevOverride) {
|
|
2103
|
-
logger.log('INFO', `[getSignedInUserPIPersonalizedMetrics] DEV OVERRIDE: Generating sample PI metrics for
|
|
2104
|
-
const sampleData = generateSamplePIPersonalizedMetrics(
|
|
2141
|
+
logger.log('INFO', `[getSignedInUserPIPersonalizedMetrics] DEV OVERRIDE: Generating sample PI metrics for effective CID ${effectiveCid}`);
|
|
2142
|
+
const sampleData = generateSamplePIPersonalizedMetrics(effectiveCid, rankEntry);
|
|
2105
2143
|
const today = new Date().toISOString().split('T')[0];
|
|
2106
2144
|
|
|
2107
2145
|
return res.status(200).json({
|
|
2108
2146
|
status: 'success',
|
|
2109
|
-
userCid: String(
|
|
2147
|
+
userCid: String(effectiveCid),
|
|
2110
2148
|
data: sampleData,
|
|
2111
2149
|
dataDate: today,
|
|
2112
2150
|
requestedDate: today,
|
|
2113
2151
|
isFallback: false,
|
|
2114
|
-
isDevOverride: true
|
|
2152
|
+
isDevOverride: true,
|
|
2153
|
+
isImpersonating: isImpersonating || false,
|
|
2154
|
+
actualCid: Number(userCid)
|
|
2115
2155
|
});
|
|
2116
2156
|
}
|
|
2117
2157
|
|
|
@@ -2123,6 +2163,7 @@ async function getSignedInUserPIPersonalizedMetrics(req, res, dependencies, conf
|
|
|
2123
2163
|
const today = new Date().toISOString().split('T')[0];
|
|
2124
2164
|
|
|
2125
2165
|
// Try to find computation with user-specific fallback logic
|
|
2166
|
+
// Use effectiveCid (impersonated or actual) for computation lookup
|
|
2126
2167
|
let foundDate = null;
|
|
2127
2168
|
let metricsData = null;
|
|
2128
2169
|
let checkedDates = [];
|
|
@@ -2146,11 +2187,14 @@ async function getSignedInUserPIPersonalizedMetrics(req, res, dependencies, conf
|
|
|
2146
2187
|
status: 'fallback',
|
|
2147
2188
|
message: 'Computation not available, use frontend fallback',
|
|
2148
2189
|
data: null,
|
|
2149
|
-
useFrontendFallback: true
|
|
2190
|
+
useFrontendFallback: true,
|
|
2191
|
+
effectiveCid: effectiveCid,
|
|
2192
|
+
isImpersonating: isImpersonating || false
|
|
2150
2193
|
});
|
|
2151
2194
|
}
|
|
2152
2195
|
|
|
2153
2196
|
// Step 2: Check if user exists in latest computation, if not, look back 7 days
|
|
2197
|
+
// Use effectiveCid for lookup
|
|
2154
2198
|
const latestDateObj = new Date(latestComputationDate + 'T00:00:00Z');
|
|
2155
2199
|
|
|
2156
2200
|
for (let daysBack = 0; daysBack <= 7; daysBack++) {
|
|
@@ -2180,13 +2224,13 @@ async function getSignedInUserPIPersonalizedMetrics(req, res, dependencies, conf
|
|
|
2180
2224
|
}
|
|
2181
2225
|
}
|
|
2182
2226
|
|
|
2183
|
-
// Check if user exists in this computation
|
|
2227
|
+
// Check if user exists in this computation (use effectiveCid)
|
|
2184
2228
|
if (computationData && typeof computationData === 'object' && !Array.isArray(computationData)) {
|
|
2185
|
-
const userData = computationData[String(
|
|
2229
|
+
const userData = computationData[String(effectiveCid)];
|
|
2186
2230
|
if (userData) {
|
|
2187
2231
|
foundDate = dateStr;
|
|
2188
2232
|
metricsData = userData;
|
|
2189
|
-
logger.log('INFO', `[getSignedInUserPIPersonalizedMetrics] Found
|
|
2233
|
+
logger.log('INFO', `[getSignedInUserPIPersonalizedMetrics] Found effective CID ${effectiveCid} in computation date ${dateStr}`);
|
|
2190
2234
|
break;
|
|
2191
2235
|
}
|
|
2192
2236
|
}
|
|
@@ -2195,23 +2239,26 @@ async function getSignedInUserPIPersonalizedMetrics(req, res, dependencies, conf
|
|
|
2195
2239
|
|
|
2196
2240
|
// Step 3: If user not found in any computation, return fallback flag
|
|
2197
2241
|
if (!foundDate || !metricsData) {
|
|
2198
|
-
logger.log('INFO', `[getSignedInUserPIPersonalizedMetrics]
|
|
2242
|
+
logger.log('INFO', `[getSignedInUserPIPersonalizedMetrics] Effective CID ${effectiveCid} not found in any computation, will use frontend fallback`);
|
|
2199
2243
|
return res.status(200).json({
|
|
2200
2244
|
status: 'fallback',
|
|
2201
2245
|
message: 'User not found in computation, use frontend fallback',
|
|
2202
2246
|
data: null,
|
|
2203
2247
|
useFrontendFallback: true,
|
|
2204
|
-
checkedDates: checkedDates
|
|
2248
|
+
checkedDates: checkedDates,
|
|
2249
|
+
effectiveCid: effectiveCid,
|
|
2250
|
+
isImpersonating: isImpersonating || false
|
|
2205
2251
|
});
|
|
2206
2252
|
}
|
|
2207
2253
|
|
|
2208
2254
|
// Step 4: Enhance with review metrics and page views from Firestore
|
|
2209
2255
|
// These require cross-collection queries that aren't in computation
|
|
2256
|
+
// Use effectiveCid for queries
|
|
2210
2257
|
|
|
2211
2258
|
// Fetch review metrics over time
|
|
2212
2259
|
const reviewsCollection = config.reviewsCollection || 'pi_reviews';
|
|
2213
2260
|
const reviewsSnapshot = await db.collection(reviewsCollection)
|
|
2214
|
-
.where('piCid', '==', Number(
|
|
2261
|
+
.where('piCid', '==', Number(effectiveCid))
|
|
2215
2262
|
.orderBy('createdAt', 'desc')
|
|
2216
2263
|
.limit(1000) // Get enough for time series
|
|
2217
2264
|
.get();
|
|
@@ -2258,8 +2305,9 @@ async function getSignedInUserPIPersonalizedMetrics(req, res, dependencies, conf
|
|
|
2258
2305
|
// Fetch profile views over time
|
|
2259
2306
|
const profileViewsCollection = config.profileViewsCollection || 'profile_views';
|
|
2260
2307
|
// Query without orderBy first, then sort in memory (to avoid index requirement)
|
|
2308
|
+
// Use effectiveCid for queries
|
|
2261
2309
|
const viewsSnapshot = await db.collection(profileViewsCollection)
|
|
2262
|
-
.where('piCid', '==', Number(
|
|
2310
|
+
.where('piCid', '==', Number(effectiveCid))
|
|
2263
2311
|
.limit(90) // Last 90 days worth of documents
|
|
2264
2312
|
.get();
|
|
2265
2313
|
|
|
@@ -2324,16 +2372,18 @@ async function getSignedInUserPIPersonalizedMetrics(req, res, dependencies, conf
|
|
|
2324
2372
|
}
|
|
2325
2373
|
};
|
|
2326
2374
|
|
|
2327
|
-
logger.log('SUCCESS', `[getSignedInUserPIPersonalizedMetrics] Returning personalized metrics for
|
|
2375
|
+
logger.log('SUCCESS', `[getSignedInUserPIPersonalizedMetrics] Returning personalized metrics for effective CID ${effectiveCid} from date ${foundDate}`);
|
|
2328
2376
|
|
|
2329
2377
|
return res.status(200).json({
|
|
2330
2378
|
status: 'success',
|
|
2331
|
-
userCid: String(
|
|
2379
|
+
userCid: String(effectiveCid),
|
|
2332
2380
|
data: metricsData,
|
|
2333
2381
|
dataDate: foundDate,
|
|
2334
2382
|
requestedDate: today,
|
|
2335
2383
|
isFallback: foundDate !== today,
|
|
2336
|
-
daysBackFromLatest: checkedDates.indexOf(foundDate)
|
|
2384
|
+
daysBackFromLatest: checkedDates.indexOf(foundDate),
|
|
2385
|
+
isImpersonating: isImpersonating || false,
|
|
2386
|
+
actualCid: Number(userCid)
|
|
2337
2387
|
});
|
|
2338
2388
|
|
|
2339
2389
|
} catch (error) {
|
|
@@ -40,6 +40,7 @@ async function getDevOverride(db, userCid, config, logger = null) {
|
|
|
40
40
|
enabled: false,
|
|
41
41
|
fakeCopiedPIs: [],
|
|
42
42
|
pretendToBePI: false,
|
|
43
|
+
impersonateCid: null, // CID to impersonate/view as
|
|
43
44
|
createdAt: FieldValue.serverTimestamp(),
|
|
44
45
|
lastUpdated: FieldValue.serverTimestamp()
|
|
45
46
|
};
|
|
@@ -58,6 +59,7 @@ async function getDevOverride(db, userCid, config, logger = null) {
|
|
|
58
59
|
enabled: false,
|
|
59
60
|
fakeCopiedPIs: [],
|
|
60
61
|
pretendToBePI: false,
|
|
62
|
+
impersonateCid: null,
|
|
61
63
|
lastUpdated: null,
|
|
62
64
|
wasAutoCreated: true
|
|
63
65
|
};
|
|
@@ -70,6 +72,7 @@ async function getDevOverride(db, userCid, config, logger = null) {
|
|
|
70
72
|
enabled: data.enabled === true,
|
|
71
73
|
fakeCopiedPIs: data.fakeCopiedPIs || [],
|
|
72
74
|
pretendToBePI: data.pretendToBePI === true,
|
|
75
|
+
impersonateCid: data.impersonateCid ? Number(data.impersonateCid) : null,
|
|
73
76
|
lastUpdated: data.lastUpdated,
|
|
74
77
|
wasAutoCreated: false
|
|
75
78
|
};
|
|
@@ -154,7 +157,7 @@ async function hasUserCopiedWithDevOverride(db, userCid, piCid, config, logger)
|
|
|
154
157
|
*/
|
|
155
158
|
async function setDevOverride(req, res, dependencies, config) {
|
|
156
159
|
const { db, logger } = dependencies;
|
|
157
|
-
const { userCid, enabled, fakeCopiedPIs, pretendToBePI } = req.body;
|
|
160
|
+
const { userCid, enabled, fakeCopiedPIs, pretendToBePI, impersonateCid } = req.body;
|
|
158
161
|
|
|
159
162
|
if (!userCid) {
|
|
160
163
|
return res.status(400).json({ error: "Missing userCid" });
|
|
@@ -177,6 +180,17 @@ async function setDevOverride(req, res, dependencies, config) {
|
|
|
177
180
|
});
|
|
178
181
|
}
|
|
179
182
|
|
|
183
|
+
// Validate impersonateCid if provided
|
|
184
|
+
if (impersonateCid !== undefined && impersonateCid !== null) {
|
|
185
|
+
const parsedCid = Number(impersonateCid);
|
|
186
|
+
if (isNaN(parsedCid) || parsedCid <= 0) {
|
|
187
|
+
return res.status(400).json({
|
|
188
|
+
error: "Invalid request",
|
|
189
|
+
message: "impersonateCid must be a positive number, or null to disable impersonation"
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
180
194
|
// Ensure all CIDs are numbers
|
|
181
195
|
const validatedPIs = enabled ? fakeCopiedPIs.map(cid => Number(cid)).filter(cid => !isNaN(cid) && cid > 0) : [];
|
|
182
196
|
|
|
@@ -193,12 +207,19 @@ async function setDevOverride(req, res, dependencies, config) {
|
|
|
193
207
|
enabled: enabled !== undefined ? (enabled === true) : (existingData.enabled || false),
|
|
194
208
|
fakeCopiedPIs: enabled !== undefined ? validatedPIs : (existingData.fakeCopiedPIs || []),
|
|
195
209
|
pretendToBePI: pretendToBePI !== undefined ? (pretendToBePI === true) : (existingData.pretendToBePI || false),
|
|
210
|
+
impersonateCid: impersonateCid !== undefined ? (impersonateCid ? Number(impersonateCid) : null) : (existingData.impersonateCid || null),
|
|
196
211
|
lastUpdated: FieldValue.serverTimestamp()
|
|
197
212
|
};
|
|
198
213
|
|
|
199
214
|
await overrideRef.set(overrideData, { merge: true });
|
|
200
215
|
|
|
201
|
-
|
|
216
|
+
const logParts = [
|
|
217
|
+
`enabled=${overrideData.enabled}`,
|
|
218
|
+
`pretendToBePI=${overrideData.pretendToBePI}`,
|
|
219
|
+
`impersonateCid=${overrideData.impersonateCid || 'null'}`,
|
|
220
|
+
`${validatedPIs.length} fake PIs`
|
|
221
|
+
];
|
|
222
|
+
logger.log('SUCCESS', `[setDevOverride] Updated dev override for user ${userCid}: ${logParts.join(', ')}`);
|
|
202
223
|
|
|
203
224
|
return res.status(200).json({
|
|
204
225
|
success: true,
|
|
@@ -248,6 +269,7 @@ async function getDevOverrideStatus(req, res, dependencies, config) {
|
|
|
248
269
|
enabled: devOverride.enabled,
|
|
249
270
|
fakeCopiedPIs: devOverride.fakeCopiedPIs,
|
|
250
271
|
pretendToBePI: devOverride.pretendToBePI || false,
|
|
272
|
+
impersonateCid: devOverride.impersonateCid || null,
|
|
251
273
|
lastUpdated: devOverride.lastUpdated,
|
|
252
274
|
wasAutoCreated: devOverride.wasAutoCreated || false
|
|
253
275
|
});
|
|
@@ -258,12 +280,36 @@ async function getDevOverrideStatus(req, res, dependencies, config) {
|
|
|
258
280
|
}
|
|
259
281
|
}
|
|
260
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Get the effective CID to use for a developer account
|
|
285
|
+
* Returns impersonateCid if set, otherwise returns the actual userCid
|
|
286
|
+
* Only works for developer accounts
|
|
287
|
+
*/
|
|
288
|
+
async function getEffectiveCid(db, userCid, config, logger = null) {
|
|
289
|
+
if (!isDeveloperAccount(userCid)) {
|
|
290
|
+
return Number(userCid); // Not a developer, return actual CID
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
294
|
+
if (devOverride && devOverride.impersonateCid) {
|
|
295
|
+
if (logger && logger.log) {
|
|
296
|
+
logger.log('INFO', `[getEffectiveCid] DEV OVERRIDE: User ${userCid} impersonating CID ${devOverride.impersonateCid}`);
|
|
297
|
+
} else {
|
|
298
|
+
console.log(`[getEffectiveCid] DEV OVERRIDE: User ${userCid} impersonating CID ${devOverride.impersonateCid}`);
|
|
299
|
+
}
|
|
300
|
+
return Number(devOverride.impersonateCid);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return Number(userCid);
|
|
304
|
+
}
|
|
305
|
+
|
|
261
306
|
module.exports = {
|
|
262
307
|
isDeveloperAccount,
|
|
263
308
|
getDevOverride,
|
|
264
309
|
getCopiedPIsWithDevOverride,
|
|
265
310
|
hasUserCopiedWithDevOverride,
|
|
266
311
|
setDevOverride,
|
|
267
|
-
getDevOverrideStatus
|
|
312
|
+
getDevOverrideStatus,
|
|
313
|
+
getEffectiveCid
|
|
268
314
|
};
|
|
269
315
|
|