Closes #ISSUE-009 - Implement DEV_MODE Offline-First Protection Shield
This commit is contained in:
@@ -347,6 +347,7 @@ export async function GET(request: Request) {
|
||||
const mode = searchParams.get('mode') || 'day_crash';
|
||||
const region = searchParams.get('region') || 'us';
|
||||
const fmpApiKey = process.env.FMP_API_KEY || 'U6lOXaOFPye7oc1D235kyAqJeQaiTAWc';
|
||||
const isDevMode = process.env.DEV_MODE === 'true';
|
||||
|
||||
let tickersPool: string[] = [];
|
||||
|
||||
@@ -358,38 +359,49 @@ export async function GET(request: Request) {
|
||||
// US region: Large/Mid pool + Small Cap pool
|
||||
tickersPool = [...US_MEGA_MID];
|
||||
|
||||
// Try to load FMP Small Caps or use static Small-Caps fallback list
|
||||
let fmpSmallCaps: string[] = [];
|
||||
try {
|
||||
fmpSmallCaps = await fetchFmpScreener(fmpApiKey);
|
||||
} catch (_) {}
|
||||
|
||||
if (fmpSmallCaps.length > 0) {
|
||||
tickersPool.push(...fmpSmallCaps);
|
||||
} else {
|
||||
if (isDevMode) {
|
||||
tickersPool.push(...US_SMALL_CAPS);
|
||||
} else {
|
||||
// Try to load FMP Small Caps or use static Small-Caps fallback list
|
||||
let fmpSmallCaps: string[] = [];
|
||||
try {
|
||||
fmpSmallCaps = await fetchFmpScreener(fmpApiKey);
|
||||
} catch (_) {}
|
||||
|
||||
if (fmpSmallCaps.length > 0) {
|
||||
tickersPool.push(...fmpSmallCaps);
|
||||
} else {
|
||||
tickersPool.push(...US_SMALL_CAPS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// De-duplicate tickers pool
|
||||
tickersPool = Array.from(new Set(tickersPool));
|
||||
|
||||
// Fetch chart details for all tickers in parallel
|
||||
const rawCharts = await Promise.allSettled(
|
||||
tickersPool.map(t => fetchYahooChart(t))
|
||||
);
|
||||
|
||||
const parsedResults: TickerDetails[] = [];
|
||||
|
||||
tickersPool.forEach((ticker, idx) => {
|
||||
const res = rawCharts[idx];
|
||||
if (res.status === 'fulfilled' && res.value) {
|
||||
parsedResults.push(res.value);
|
||||
} else {
|
||||
// Fetch failed, use high-fidelity simulation
|
||||
if (isDevMode) {
|
||||
// Bypass Yahoo fetches completely, generate simulated charts directly!
|
||||
tickersPool.forEach((ticker) => {
|
||||
parsedResults.push(generateSimulatedChart(ticker, mode));
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Fetch chart details for all tickers in parallel
|
||||
const rawCharts = await Promise.allSettled(
|
||||
tickersPool.map(t => fetchYahooChart(t))
|
||||
);
|
||||
|
||||
tickersPool.forEach((ticker, idx) => {
|
||||
const res = rawCharts[idx];
|
||||
if (res.status === 'fulfilled' && res.value) {
|
||||
parsedResults.push(res.value);
|
||||
} else {
|
||||
// Fetch failed, use high-fidelity simulation
|
||||
parsedResults.push(generateSimulatedChart(ticker, mode));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch fundamentals for top 15 candidates to reduce payload weight
|
||||
// Sort candidates first based on mode to isolate top 15
|
||||
@@ -409,19 +421,26 @@ export async function GET(request: Request) {
|
||||
// Overlay fundamentals
|
||||
const finalResults = await Promise.all(
|
||||
sortedCandidates.map(async (item) => {
|
||||
const fund = top15Symbols.has(item.ticker)
|
||||
? await fetchFMPFundamentalData(item.ticker, fmpApiKey)
|
||||
: getMockFundamentals(item.ticker);
|
||||
const useMock = isDevMode || !top15Symbols.has(item.ticker);
|
||||
const fund = useMock
|
||||
? getMockFundamentals(item.ticker)
|
||||
: await fetchFMPFundamentalData(item.ticker, fmpApiKey);
|
||||
|
||||
return {
|
||||
...item,
|
||||
...fund,
|
||||
dividendYield: fund.dividendYield ? Number((fund.dividendYield * (top15Symbols.has(item.ticker) ? 1 : 100)).toFixed(2)) : 0
|
||||
dividendYield: fund.dividendYield ? Number((fund.dividendYield * (useMock ? 100 : 1)).toFixed(2)) : 0
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const response = NextResponse.json({ results: finalResults });
|
||||
const response = NextResponse.json({
|
||||
results: finalResults,
|
||||
isShieldActive: isDevMode
|
||||
});
|
||||
response.headers.set('Cache-Control', 'no-store, max-age=0, must-revalidate');
|
||||
if (isDevMode) {
|
||||
response.headers.set('X-Shield-Active', 'true');
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user