diff --git a/lib/api.ts b/lib/api.ts index 9c1a32d..9340a0c 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -7,10 +7,7 @@ import { WBNB_ADDRESS } from '@/constants' import { getRandomElementFromArray } from '@/lib/utils' const client = axios.create({ - baseURL: 'https://api.etherscan.io', - params: { - chainId: 56, - }, + baseURL: 'https://api.bscscan.com/api', }) client.interceptors.request.use( @@ -92,7 +89,7 @@ export async function getTokenPrice({ symbol, address }: { symbol: string, addre } export async function getBlockNumberByTimestamp(timestamp: number) { - const res = await client.get('/v2/api', { + const res = await client.get('', { params: { module: 'block', action: 'getblocknobytime', @@ -129,7 +126,7 @@ export async function getTransactions({ startblock?: number endblock?: number }): Promise { - const res = await client.get<{ result: TransactionActionMap[T][] }>('/v2/api', { + const res = await client.get<{ result: TransactionActionMap[T][] }>('', { params: { module: 'account', action, diff --git a/middleware.ts b/middleware.ts index 9f9468c..af08f3f 100644 --- a/middleware.ts +++ b/middleware.ts @@ -5,14 +5,61 @@ export function middleware(request: NextRequest) { if (request.nextUrl.pathname.startsWith('/api/')) { const origin = request.headers.get('origin') const referer = request.headers.get('referer') - const allowedDomains = ['http://localhost:3000', 'https://bn-alpha.site', 'https://www.bn-alpha.site'] - const isValidOrigin = origin && allowedDomains.includes(origin) - const isValidReferer = referer && allowedDomains.some(domain => referer.startsWith(domain)) + const host = request.headers.get('host') + const forwardedHost = request.headers.get('x-forwarded-host') + const forwardedProto = request.headers.get('x-forwarded-proto') + const defaultAllowedDomains = ['http://localhost:3000', 'https://bn-alpha.site', 'https://www.bn-alpha.site'] + const dynamicAllowedDomains = new Set(defaultAllowedDomains) - if (!isValidOrigin && !isValidReferer) { - return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) + const normalizeProtocol = (protocol?: string | null) => { + if (!protocol) + return null + return protocol.endsWith(':') ? protocol.slice(0, -1) : protocol + } + + const appendOrigin = (hostValue?: string | null, protocol?: string | null) => { + if (!hostValue) + return + const normalizedProtocol = normalizeProtocol(protocol) ?? 'https' + const baseHost = hostValue.split(',')[0]?.trim() + if (!baseHost) + return + dynamicAllowedDomains.add(`${normalizedProtocol}://${baseHost}`) } + if (request.nextUrl.origin) + dynamicAllowedDomains.add(request.nextUrl.origin) + + appendOrigin(request.nextUrl.host, request.nextUrl.protocol) + appendOrigin(host, forwardedProto ?? request.nextUrl.protocol) + appendOrigin(forwardedHost, forwardedProto) + + const allowedDomains = Array.from(dynamicAllowedDomains) + + const getRefererOrigin = (value?: string | null) => { + if (!value) + return null + try { + return new URL(value).origin + } + catch { + return null + } + } + + const refererOrigin = getRefererOrigin(referer) + const inferredSameHostOrigin = request.nextUrl.host + ? `${normalizeProtocol(request.nextUrl.protocol) ?? 'https'}://${request.nextUrl.host}` + : null + + const isValidOrigin = !!origin && dynamicAllowedDomains.has(origin) + const isValidReferer = !!refererOrigin && dynamicAllowedDomains.has(refererOrigin) + const isMissingCorsHeaders = !origin && !referer + const isSameHostRequest = isMissingCorsHeaders && !!inferredSameHostOrigin && dynamicAllowedDomains.has(inferredSameHostOrigin) + + if (!isValidOrigin && !isValidReferer && !isSameHostRequest) + return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) + const userAgent = request.headers.get('user-agent') const blockedUserAgents = ['curl/', 'wget/', 'python-requests/', 'postman', 'insomnia', 'httpie'] @@ -22,8 +69,14 @@ export function middleware(request: NextRequest) { const response = NextResponse.next() + const fallbackOrigin = request.nextUrl.origin ?? inferredSameHostOrigin ?? allowedDomains[0] + const corsOrigin = (isValidOrigin && origin) + || (isValidReferer && refererOrigin) + || (isSameHostRequest && inferredSameHostOrigin) + || fallbackOrigin + // CORS Headers - response.headers.set('Access-Control-Allow-Origin', origin || allowedDomains[0]) + response.headers.set('Access-Control-Allow-Origin', corsOrigin) response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key') response.headers.set('Access-Control-Max-Age', '86400')