/** * Cloudflare Pages Function * /api/nse * Full Market Proxy (NSE + BSE + MCX) */ const CORS = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, OPTIONS", "Content-Type": "application/json", "Cache-Control": "no-store" }; /* NSE Index Mapping */ const NSE_INDEX = { NIFTY: "NIFTY 50", BANKNIFTY: "NIFTY BANK", FINNIFTY: "NIFTY FIN SERVICE", MIDCPNIFTY: "NIFTY MIDCAP SELECT", NIFTYNXT50: "NIFTY NEXT 50" }; /* ───────── NSE FETCH WITH SESSION ───────── */ async function fetchNSE(url) { const headers = { "User-Agent": "Mozilla/5.0", "Accept": "application/json", "Referer": "https://www.nseindia.com/" }; const home = await fetch("https://www.nseindia.com", { headers }); const cookie = home.headers.get("set-cookie"); const res = await fetch(url, { headers: { ...headers, "Cookie": cookie || "" } }); if (!res.ok) throw new Error("NSE HTTP " + res.status); return res.json(); } /* ───────── SPOT DATA (ALL MARKETS) ───────── */ async function getSpots() { const spots = {}; /* NSE INDICES */ try { const data = await fetchNSE( "https://www.nseindia.com/api/allIndices" ); for (const row of data.data || []) { for (const [key, name] of Object.entries(NSE_INDEX)) { if (row.index === name && row.last) { spots[key] = parseFloat(row.last); } } } } catch (err) {} /* BSE SENSEX */ try { const res = await fetch( "https://api.bseindia.com/BseIndiaAPI/api/GetSensexData/w" ); const data = await res.json(); if (data?.Data?.CurrVal) { spots["SENSEX"] = parseFloat(data.Data.CurrVal.replace(/,/g, "")); } } catch (err) {} /* MCX COMMODITIES */ try { const res = await fetch( "https://www.mcxindia.com/api/marketplace/getBestIndicativeRates" ); const data = await res.json(); const list = data?.data || []; for (const item of list) { const sym = (item.symbol || "").toUpperCase(); const price = parseFloat(item.rate || 0); if (sym.includes("GOLDM")) spots["GOLDMINI"] = price; if (sym.includes("GOLD") && !sym.includes("MINI")) spots["GOLD"] = price; if (sym.includes("SILVERM")) spots["SILVERMINI"] = price; if (sym.includes("SILVER")) spots["SILVER"] = price; if (sym.includes("CRUDE")) spots["CRUDEOIL"] = price; if (sym.includes("NATURAL")) spots["NATURALGAS"] = price; if (sym.includes("COPPER")) spots["COPPER"] = price; if (sym.includes("ALUMINIUM")) spots["ALUMINIUM"] = price; } } catch (err) {} return spots; } /* ───────── OPTION CHAIN (NSE ONLY) ───────── */ async function getChain(symbol, expiry) { const url = `https://www.nseindia.com/api/option-chain-indices?symbol=${symbol}`; const data = await fetchNSE(url); const records = data.records?.data || []; const expiryDates = data.records?.expiryDates || []; const filtered = expiry ? records.filter(r => r.expiryDate === expiry) : records; const strikes = filtered.map(r => ({ strike: r.strikePrice, CE: r.CE ? { ltp: r.CE.lastPrice, iv: r.CE.impliedVolatility, oi: r.CE.openInterest, volume: r.CE.totalTradedVolume, change: r.CE.change } : null, PE: r.PE ? { ltp: r.PE.lastPrice, iv: r.PE.impliedVolatility, oi: r.PE.openInterest, volume: r.PE.totalTradedVolume, change: r.PE.change } : null })); return { symbol, spot: data.records?.underlyingValue || null, expiryDates, strikes }; } /* ───────── MAIN HANDLER ───────── */ export async function onRequest({ request }) { if (request.method === "OPTIONS") return new Response(null, { status: 204, headers: CORS }); const url = new URL(request.url); const type = url.searchParams.get("type"); try { /* SPOT */ if (type === "spot") { const spots = await getSpots(); return new Response( JSON.stringify({ ok: true, spots }), { headers: CORS } ); } /* OPTION CHAIN */ if (type === "chain") { const symbol = url.searchParams.get("symbol") || "NIFTY"; const expiry = url.searchParams.get("expiry") || null; const chain = await getChain(symbol, expiry); return new Response( JSON.stringify({ ok: true, ...chain }), { headers: CORS } ); } return new Response( JSON.stringify({ ok: false }), { status: 400, headers: CORS } ); } catch (err) { return new Response( JSON.stringify({ ok: false, error: err.message }), { status: 502, headers: CORS } ); } }