borsa / nextjs-app /src /app /api /movers /route.ts
veteroner's picture
feat: complete US market bilingual support across all pages, components, and API routes
20f3b43
import { NextRequest, NextResponse } from 'next/server'
import { apiUrl, API_BASE } from '@/lib/runtime-config'
import { requireAuth } from '@/lib/api-auth'
import { getStockQuote } from '@/lib/yahoo-finance'
export const dynamic = 'force-dynamic'
const US_MOVERS_POOL = [
'AAPL','MSFT','GOOGL','AMZN','NVDA','META','TSLA','BRK-B','JPM',
'V','JNJ','WMT','PG','MA','UNH','HD','DIS','BAC','XOM','PFE',
'NFLX','INTC','AMD','CRM','PYPL','ADBE','COST','CSCO','CMCSA','ABT',
]
export async function GET(_req: NextRequest) {
const auth = await requireAuth(_req)
if (!auth.authenticated) return auth.response
const { searchParams } = new URL(_req.url)
const market = (searchParams.get('market') || 'bist') as 'bist' | 'us'
if (market === 'us') {
try {
const results = await Promise.allSettled(
US_MOVERS_POOL.map(async (sym) => {
const q = await getStockQuote(sym, 'us')
return { symbol: sym, last_price: q.price, change_pct: q.changePercent, volume: q.volume }
})
)
const stocks = results
.filter((r): r is PromiseFulfilledResult<{ symbol: string; last_price: number; change_pct: number; volume: number }> => r.status === 'fulfilled')
.map((r) => r.value)
const sorted = [...stocks].sort((a, b) => b.change_pct - a.change_pct)
const gainers = sorted.filter((s) => s.change_pct > 0).slice(0, 10)
const losers = sorted.filter((s) => s.change_pct < 0).sort((a, b) => a.change_pct - b.change_pct).slice(0, 10)
return NextResponse.json({ ok: true, gainers, losers }, { status: 200 })
} catch (e: unknown) {
const message = e instanceof Error ? e.message : 'Unknown'
return NextResponse.json({ ok: false, error: `US movers failed: ${message}`, gainers: [], losers: [] }, { status: 502 })
}
}
try {
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 30000)
let resp: Response
try {
resp = await fetch(apiUrl('/api/movers'), {
headers: { accept: 'application/json' },
next: { revalidate: 300 },
signal: controller.signal,
})
} finally {
clearTimeout(timeout)
}
if (!resp.ok) {
return NextResponse.json(
{ ok: false, error: `Movers backend failed (HTTP ${resp.status})`, gainers: [], losers: [] },
{ status: 502 }
)
}
const payload = await resp.json().catch(() => null)
if (!payload) {
return NextResponse.json(
{ ok: false, error: 'Invalid payload from movers backend', gainers: [], losers: [] },
{ status: 502 }
)
}
const payloadObj = payload as Record<string, unknown>
const gainers = Array.isArray(payloadObj?.gainers) ? payloadObj.gainers as unknown[] : []
const losers = Array.isArray(payloadObj?.losers) ? payloadObj.losers as unknown[] : []
return NextResponse.json({ ok: true, gainers, losers }, { status: 200 })
} catch (e: unknown) {
const message = e instanceof Error ? e.message : 'Unknown'
return NextResponse.json(
{ ok: false, error: `Movers fetch failed: ${message}`, gainers: [], losers: [] },
{ status: 502 }
)
}
}