bulltrackers-module 1.0.419 → 1.0.421
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.
|
@@ -835,55 +835,70 @@ async function autoGenerateWatchlist(req, res, dependencies, config) {
|
|
|
835
835
|
try {
|
|
836
836
|
let copiedPIs = [];
|
|
837
837
|
let dataSource = 'unknown';
|
|
838
|
+
let isDevOverride = false;
|
|
838
839
|
const today = new Date().toISOString().split('T')[0];
|
|
839
840
|
|
|
840
|
-
// ===
|
|
841
|
-
const
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
db,
|
|
850
|
-
insightsCollection,
|
|
851
|
-
resultsSub,
|
|
852
|
-
compsSub,
|
|
853
|
-
category,
|
|
854
|
-
computationName,
|
|
855
|
-
userCid,
|
|
856
|
-
30
|
|
857
|
-
);
|
|
841
|
+
// === DEV OVERRIDE CHECK (for developer accounts) ===
|
|
842
|
+
const { getCopiedPIsWithDevOverride } = require('./dev_helpers');
|
|
843
|
+
const devResult = await getCopiedPIsWithDevOverride(db, userCid, config, logger);
|
|
844
|
+
if (devResult.isDevOverride) {
|
|
845
|
+
copiedPIs = devResult.copiedPIs;
|
|
846
|
+
dataSource = devResult.dataSource;
|
|
847
|
+
isDevOverride = true;
|
|
848
|
+
logger.log('INFO', `[autoGenerateWatchlist] Using DEV OVERRIDE for user ${userCid}, found ${copiedPIs.length} fake copied PIs`);
|
|
849
|
+
}
|
|
858
850
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
.doc(computationName);
|
|
851
|
+
// === PRIMARY: Try to fetch from computation (cheaper/faster) ===
|
|
852
|
+
// Only if dev override is not active
|
|
853
|
+
if (!isDevOverride) {
|
|
854
|
+
const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
|
|
855
|
+
const category = 'signed_in_user';
|
|
856
|
+
const computationName = 'SignedInUserCopiedPIs';
|
|
866
857
|
|
|
867
|
-
|
|
858
|
+
// Try to find latest computation date (with fallback)
|
|
859
|
+
const resultsSub = config.resultsSubcollection || 'results';
|
|
860
|
+
const compsSub = config.computationsSubcollection || 'computations';
|
|
861
|
+
const computationDate = await findLatestComputationDate(
|
|
862
|
+
db,
|
|
863
|
+
insightsCollection,
|
|
864
|
+
resultsSub,
|
|
865
|
+
compsSub,
|
|
866
|
+
category,
|
|
867
|
+
computationName,
|
|
868
|
+
userCid,
|
|
869
|
+
30
|
|
870
|
+
);
|
|
868
871
|
|
|
869
|
-
if (
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
+
if (computationDate) {
|
|
873
|
+
const computationRef = db.collection(insightsCollection)
|
|
874
|
+
.doc(computationDate)
|
|
875
|
+
.collection('results')
|
|
876
|
+
.doc(category)
|
|
877
|
+
.collection('computations')
|
|
878
|
+
.doc(computationName);
|
|
872
879
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
880
|
+
const computationDoc = await computationRef.get();
|
|
881
|
+
|
|
882
|
+
if (computationDoc.exists) {
|
|
883
|
+
const computationData = computationDoc.data();
|
|
884
|
+
const userResult = computationData[String(userCid)];
|
|
885
|
+
|
|
886
|
+
if (userResult && userResult.current && userResult.current.length > 0) {
|
|
887
|
+
// Convert computation result to our format
|
|
888
|
+
copiedPIs = userResult.current.map(cid => ({
|
|
889
|
+
cid: Number(cid),
|
|
890
|
+
username: 'Unknown' // Username not in computation, will get from rankings
|
|
891
|
+
}));
|
|
892
|
+
dataSource = 'computation';
|
|
893
|
+
logger.log('INFO', `[autoGenerateWatchlist] Using computation data (date: ${computationDate}) for user ${userCid}, found ${copiedPIs.length} copied PIs`);
|
|
894
|
+
}
|
|
881
895
|
}
|
|
882
896
|
}
|
|
883
897
|
}
|
|
884
898
|
|
|
885
899
|
// === FALLBACK: Read portfolio data directly if computation not available ===
|
|
886
|
-
if
|
|
900
|
+
// Only if dev override is not active and no computation data found
|
|
901
|
+
if (!isDevOverride && copiedPIs.length === 0) {
|
|
887
902
|
logger.log('INFO', `[autoGenerateWatchlist] Computation not available, falling back to direct portfolio read for user ${userCid}`);
|
|
888
903
|
|
|
889
904
|
const { signedInUsersCollection } = config;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Developer Mode Helpers
|
|
3
|
+
* Allows specific developer accounts to override copied PI data for testing
|
|
4
|
+
* SECURITY: Only works for whitelisted developer CIDs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { FieldValue } = require('@google-cloud/firestore');
|
|
8
|
+
|
|
9
|
+
// Whitelist of developer account CIDs that can use dev mode
|
|
10
|
+
const DEVELOPER_CIDS = [
|
|
11
|
+
29312236 // Aiden Hawkins - marau2021
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if a user CID is a developer account
|
|
16
|
+
*/
|
|
17
|
+
function isDeveloperAccount(userCid) {
|
|
18
|
+
return DEVELOPER_CIDS.includes(Number(userCid));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get developer override data for a user
|
|
23
|
+
* Auto-creates default document if it doesn't exist (for developer accounts only)
|
|
24
|
+
* Returns null if not a developer
|
|
25
|
+
*/
|
|
26
|
+
async function getDevOverride(db, userCid, config, logger = null) {
|
|
27
|
+
if (!isDeveloperAccount(userCid)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const devOverridesCollection = config.devOverridesCollection || 'dev_overrides';
|
|
33
|
+
const overrideRef = db.collection(devOverridesCollection).doc(String(userCid));
|
|
34
|
+
const overrideDoc = await overrideRef.get();
|
|
35
|
+
|
|
36
|
+
// Auto-create default document if it doesn't exist
|
|
37
|
+
if (!overrideDoc.exists) {
|
|
38
|
+
const defaultData = {
|
|
39
|
+
userCid: Number(userCid),
|
|
40
|
+
enabled: false,
|
|
41
|
+
fakeCopiedPIs: [],
|
|
42
|
+
createdAt: FieldValue.serverTimestamp(),
|
|
43
|
+
lastUpdated: FieldValue.serverTimestamp()
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
await overrideRef.set(defaultData);
|
|
47
|
+
|
|
48
|
+
const logMsg = `[getDevOverride] Auto-created default dev override document for developer account ${userCid}`;
|
|
49
|
+
if (logger && logger.log) {
|
|
50
|
+
logger.log('INFO', logMsg);
|
|
51
|
+
} else {
|
|
52
|
+
console.log(logMsg);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Return the default (disabled) override
|
|
56
|
+
return {
|
|
57
|
+
enabled: false,
|
|
58
|
+
fakeCopiedPIs: [],
|
|
59
|
+
lastUpdated: null,
|
|
60
|
+
wasAutoCreated: true
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = overrideDoc.data();
|
|
65
|
+
|
|
66
|
+
// Return override data (even if disabled, so caller knows it exists)
|
|
67
|
+
return {
|
|
68
|
+
enabled: data.enabled === true,
|
|
69
|
+
fakeCopiedPIs: data.fakeCopiedPIs || [],
|
|
70
|
+
lastUpdated: data.lastUpdated,
|
|
71
|
+
wasAutoCreated: false
|
|
72
|
+
};
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('[getDevOverride] Error fetching/creating dev override:', error);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the list of copied PIs for a user, with dev override support
|
|
81
|
+
* Returns { copiedPIs: Array, isDevOverride: boolean, dataSource: string }
|
|
82
|
+
*/
|
|
83
|
+
async function getCopiedPIsWithDevOverride(db, userCid, config, logger) {
|
|
84
|
+
const userCidNum = Number(userCid);
|
|
85
|
+
|
|
86
|
+
// Check for dev override first (this will auto-create default if needed)
|
|
87
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
88
|
+
|
|
89
|
+
if (devOverride && devOverride.enabled && devOverride.fakeCopiedPIs.length > 0) {
|
|
90
|
+
if (logger && logger.log) {
|
|
91
|
+
logger.log('INFO', `[getCopiedPIsWithDevOverride] Using DEV OVERRIDE for user ${userCid}: ${devOverride.fakeCopiedPIs.length} fake copied PIs`);
|
|
92
|
+
} else {
|
|
93
|
+
console.log(`[getCopiedPIsWithDevOverride] Using DEV OVERRIDE for user ${userCid}: ${devOverride.fakeCopiedPIs.length} fake copied PIs`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
copiedPIs: devOverride.fakeCopiedPIs.map(cid => ({
|
|
98
|
+
cid: Number(cid),
|
|
99
|
+
username: 'Dev Override' // Will be resolved from rankings later
|
|
100
|
+
})),
|
|
101
|
+
isDevOverride: true,
|
|
102
|
+
dataSource: 'dev_override'
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Otherwise, use normal logic (computation or portfolio)
|
|
107
|
+
// This will be handled by the calling function
|
|
108
|
+
return {
|
|
109
|
+
copiedPIs: [],
|
|
110
|
+
isDevOverride: false,
|
|
111
|
+
dataSource: 'normal'
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Check if a user has copied a specific PI, with dev override support
|
|
117
|
+
*/
|
|
118
|
+
async function hasUserCopiedWithDevOverride(db, userCid, piCid, config, logger) {
|
|
119
|
+
const userCidNum = Number(userCid);
|
|
120
|
+
const piCidNum = Number(piCid);
|
|
121
|
+
|
|
122
|
+
// Check for dev override first (this will auto-create default if needed)
|
|
123
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
124
|
+
|
|
125
|
+
if (devOverride && devOverride.enabled) {
|
|
126
|
+
const hasCopied = devOverride.fakeCopiedPIs.includes(piCidNum);
|
|
127
|
+
if (hasCopied) {
|
|
128
|
+
if (logger && logger.log) {
|
|
129
|
+
logger.log('INFO', `[hasUserCopiedWithDevOverride] DEV OVERRIDE: User ${userCid} "copies" PI ${piCid}`);
|
|
130
|
+
} else {
|
|
131
|
+
console.log(`[hasUserCopiedWithDevOverride] DEV OVERRIDE: User ${userCid} "copies" PI ${piCid}`);
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
// If dev override is enabled but PI not in list, return false (don't check real data)
|
|
136
|
+
if (logger && logger.log) {
|
|
137
|
+
logger.log('INFO', `[hasUserCopiedWithDevOverride] DEV OVERRIDE: User ${userCid} does NOT "copy" PI ${piCid} (override enabled, PI not in fake list)`);
|
|
138
|
+
} else {
|
|
139
|
+
console.log(`[hasUserCopiedWithDevOverride] DEV OVERRIDE: User ${userCid} does NOT "copy" PI ${piCid} (override enabled, PI not in fake list)`);
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Otherwise, use normal logic (will be handled by calling function)
|
|
145
|
+
return null; // null means "not a dev override, use normal logic"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Create or update dev override for a developer account
|
|
150
|
+
* SECURITY: Only works for whitelisted developer CIDs
|
|
151
|
+
*/
|
|
152
|
+
async function setDevOverride(req, res, dependencies, config) {
|
|
153
|
+
const { db, logger } = dependencies;
|
|
154
|
+
const { userCid, enabled, fakeCopiedPIs } = req.body;
|
|
155
|
+
|
|
156
|
+
if (!userCid) {
|
|
157
|
+
return res.status(400).json({ error: "Missing userCid" });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// SECURITY CHECK: Only allow whitelisted developer accounts
|
|
161
|
+
if (!isDeveloperAccount(userCid)) {
|
|
162
|
+
logger.log('WARN', `[setDevOverride] Unauthorized attempt to set dev override for user ${userCid}`);
|
|
163
|
+
return res.status(403).json({
|
|
164
|
+
error: "Forbidden",
|
|
165
|
+
message: "Developer mode is only available for authorized developer accounts"
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Validate fakeCopiedPIs
|
|
170
|
+
if (enabled && (!Array.isArray(fakeCopiedPIs) || fakeCopiedPIs.length === 0)) {
|
|
171
|
+
return res.status(400).json({
|
|
172
|
+
error: "Invalid request",
|
|
173
|
+
message: "When enabled, fakeCopiedPIs must be a non-empty array"
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Ensure all CIDs are numbers
|
|
178
|
+
const validatedPIs = enabled ? fakeCopiedPIs.map(cid => Number(cid)).filter(cid => !isNaN(cid) && cid > 0) : [];
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const devOverridesCollection = config.devOverridesCollection || 'dev_overrides';
|
|
182
|
+
const overrideRef = db.collection(devOverridesCollection).doc(String(userCid));
|
|
183
|
+
|
|
184
|
+
const overrideData = {
|
|
185
|
+
userCid: Number(userCid),
|
|
186
|
+
enabled: enabled === true,
|
|
187
|
+
fakeCopiedPIs: validatedPIs,
|
|
188
|
+
lastUpdated: FieldValue.serverTimestamp()
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
await overrideRef.set(overrideData, { merge: true });
|
|
192
|
+
|
|
193
|
+
logger.log('SUCCESS', `[setDevOverride] Updated dev override for user ${userCid}: enabled=${enabled}, ${validatedPIs.length} fake PIs`);
|
|
194
|
+
|
|
195
|
+
return res.status(200).json({
|
|
196
|
+
success: true,
|
|
197
|
+
message: "Developer override updated",
|
|
198
|
+
override: overrideData
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
} catch (error) {
|
|
202
|
+
logger.log('ERROR', `[setDevOverride] Error updating dev override:`, error);
|
|
203
|
+
return res.status(500).json({ error: error.message });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get current dev override status for a developer account
|
|
209
|
+
*/
|
|
210
|
+
async function getDevOverrideStatus(req, res, dependencies, config) {
|
|
211
|
+
const { db, logger } = dependencies;
|
|
212
|
+
const { userCid } = req.query;
|
|
213
|
+
|
|
214
|
+
if (!userCid) {
|
|
215
|
+
return res.status(400).json({ error: "Missing userCid" });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// SECURITY CHECK: Only allow whitelisted developer accounts
|
|
219
|
+
if (!isDeveloperAccount(userCid)) {
|
|
220
|
+
return res.status(403).json({
|
|
221
|
+
error: "Forbidden",
|
|
222
|
+
message: "Developer mode is only available for authorized developer accounts"
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
// This will auto-create default document if it doesn't exist
|
|
228
|
+
const devOverride = await getDevOverride(db, userCid, config, logger);
|
|
229
|
+
|
|
230
|
+
if (!devOverride) {
|
|
231
|
+
// Should not happen for developer accounts, but handle gracefully
|
|
232
|
+
return res.status(200).json({
|
|
233
|
+
enabled: false,
|
|
234
|
+
fakeCopiedPIs: [],
|
|
235
|
+
message: "No dev override configured"
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return res.status(200).json({
|
|
240
|
+
enabled: devOverride.enabled,
|
|
241
|
+
fakeCopiedPIs: devOverride.fakeCopiedPIs,
|
|
242
|
+
lastUpdated: devOverride.lastUpdated,
|
|
243
|
+
wasAutoCreated: devOverride.wasAutoCreated || false
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
} catch (error) {
|
|
247
|
+
logger.log('ERROR', `[getDevOverrideStatus] Error fetching dev override:`, error);
|
|
248
|
+
return res.status(500).json({ error: error.message });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = {
|
|
253
|
+
isDeveloperAccount,
|
|
254
|
+
getDevOverride,
|
|
255
|
+
getCopiedPIsWithDevOverride,
|
|
256
|
+
hasUserCopiedWithDevOverride,
|
|
257
|
+
setDevOverride,
|
|
258
|
+
getDevOverrideStatus
|
|
259
|
+
};
|
|
260
|
+
|
|
@@ -70,7 +70,7 @@ async function findLatestComputationDate(db, insightsCollection, resultsSub, com
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Checks if a Signed-In User has ever copied a specific PI.
|
|
73
|
-
* Uses
|
|
73
|
+
* Uses dev override (if enabled) > SignedInUserCopiedPIs computation > direct portfolio check.
|
|
74
74
|
*/
|
|
75
75
|
async function hasUserCopied(db, userCid, piCid, config) {
|
|
76
76
|
const { signedInUsersCollection } = config;
|
|
@@ -78,6 +78,15 @@ async function hasUserCopied(db, userCid, piCid, config) {
|
|
|
78
78
|
const userCidNum = Number(userCid);
|
|
79
79
|
|
|
80
80
|
try {
|
|
81
|
+
// === DEV OVERRIDE CHECK (for developer accounts) ===
|
|
82
|
+
const { hasUserCopiedWithDevOverride } = require('./dev_helpers');
|
|
83
|
+
const devResult = await hasUserCopiedWithDevOverride(db, userCid, piCid, config, console);
|
|
84
|
+
if (devResult !== null) {
|
|
85
|
+
// devResult is true/false (not null), meaning dev override is active
|
|
86
|
+
return devResult;
|
|
87
|
+
}
|
|
88
|
+
// devResult is null, meaning no dev override, continue with normal logic
|
|
89
|
+
|
|
81
90
|
// === PRIMARY: Try to fetch from SignedInUserCopiedPIs computation ===
|
|
82
91
|
const insightsCollection = config.unifiedInsightsCollection || 'unified_insights';
|
|
83
92
|
const category = 'signed_in_user';
|
|
@@ -8,6 +8,7 @@ const { getPiAnalytics, getUserRecommendations, getWatchlist, updateWatchlist, a
|
|
|
8
8
|
const { initiateVerification, finalizeVerification } = require('./helpers/verification_helpers');
|
|
9
9
|
const { getUserWatchlists, getWatchlist: getWatchlistById, createWatchlist, updateWatchlist: updateWatchlistById, deleteWatchlist, copyWatchlist, getPublicWatchlists } = require('./helpers/watchlist_helpers');
|
|
10
10
|
const { subscribeToAlerts, updateSubscription, unsubscribeFromAlerts, getUserSubscriptions, subscribeToWatchlist } = require('./helpers/subscription_helpers');
|
|
11
|
+
const { setDevOverride, getDevOverrideStatus } = require('./helpers/dev_helpers');
|
|
11
12
|
|
|
12
13
|
module.exports = (dependencies, config) => {
|
|
13
14
|
const router = express.Router();
|
|
@@ -67,5 +68,9 @@ module.exports = (dependencies, config) => {
|
|
|
67
68
|
router.get('/me/watchlists/:id/trigger-counts', (req, res) => getWatchlistTriggerCounts(req, res, dependencies, config));
|
|
68
69
|
router.get('/me/watchlists/:id/rankings-check', (req, res) => checkPisInRankings(req, res, dependencies, config));
|
|
69
70
|
|
|
71
|
+
// --- Developer Mode (only for whitelisted developer accounts) ---
|
|
72
|
+
router.post('/dev/override', (req, res) => setDevOverride(req, res, dependencies, config));
|
|
73
|
+
router.get('/dev/override', (req, res) => getDevOverrideStatus(req, res, dependencies, config));
|
|
74
|
+
|
|
70
75
|
return router;
|
|
71
76
|
};
|