From d0c6984205c5a50468f0b43bdeab2717098e184a Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Sun, 31 Aug 2025 19:14:37 -0600 Subject: [PATCH] attempt febbox proxy Update febbox-proxy.ts Update febbox-proxy.ts Update febbox-proxy.ts --- src/routes/febbox-proxy.ts | 166 +++++++++++++++++++++++++++++++++++++ src/utils/headers.ts | 11 ++- 2 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/routes/febbox-proxy.ts diff --git a/src/routes/febbox-proxy.ts b/src/routes/febbox-proxy.ts new file mode 100644 index 0000000..0bd6950 --- /dev/null +++ b/src/routes/febbox-proxy.ts @@ -0,0 +1,166 @@ +import { getBodyBuffer } from '@/utils/body'; +import { + getProxyHeaders, + getAfterResponseHeaders, + getBlacklistedHeaders, +} from '@/utils/headers'; +import { + createTokenIfNeeded, + isAllowedToMakeRequest, + setTokenHeader, +} from '@/utils/turnstile'; +import { sendJson } from '@/utils/sending'; +import { setResponseHeaders } from 'h3'; + +export default defineEventHandler(async (event) => { + // Handle preflight CORS requests + if (isPreflightRequest(event)) { + handleCors(event, {}); + event.node.res.statusCode = 204; + event.node.res.end(); + return; + } + + // Reject any other OPTIONS requests + if (event.node.req.method === 'OPTIONS') { + throw createError({ + statusCode: 405, + statusMessage: 'Method Not Allowed', + }); + } + + // Parse URL parameter + const destination = getQuery<{ url?: string }>(event).url; + if (!destination) { + return await sendJson({ + event, + status: 200, + data: { + message: `Febbox proxy is working as expected (v${ + useRuntimeConfig(event).version + })`, + }, + }); + } + + // Check if allowed to make the request + if (!(await isAllowedToMakeRequest(event))) { + return await sendJson({ + event, + status: 401, + data: { + error: 'Invalid or missing token', + }, + }); + } + + // Read body and create token if needed + const body = await getBodyBuffer(event); + const token = await createTokenIfNeeded(event); + + try { + // For Febbox, we need to handle cookies and redirects properly + const url = new URL(destination); + + // Get certificate and key from environment variables + let certHeader = ''; + let keyHeader = ''; + const cert = process.env.CERT_CONTENT; + const key = process.env.KEY_CONTENT; + + if (cert && key) { + // Encode certificate and key content for use in headers + certHeader = Buffer.from(cert).toString('base64'); + keyHeader = Buffer.from(key).toString('base64'); + } else { + console.log('Certificate or key environment variables not found, proceeding without custom certificate'); + } + + // First, make a request to get the session cookie + const sessionHeaders: any = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language': 'en-US,en;q=0.9', + 'Accept-Encoding': 'gzip, deflate, br', + 'Connection': 'keep-alive', + 'Upgrade-Insecure-Requests': '1', + }; + + // Add certificate headers if available + if (certHeader) { + sessionHeaders['X-Certificate'] = certHeader; + } + if (keyHeader) { + sessionHeaders['X-Private-Key'] = keyHeader; + } + + const sessionResponse = await globalThis.fetch(url.origin, { + method: 'GET', + headers: sessionHeaders, + redirect: 'follow', + credentials: 'include', + }); + + // Extract cookies from the session response + const cookies = sessionResponse.headers.get('set-cookie'); + let cookieHeader = ''; + + if (cookies) { + // Extract PHPSESSID cookie if present + const cookieMatch = cookies.match(/PHPSESSID=([^;]+)/); + if (cookieMatch) { + cookieHeader = `PHPSESSID=${cookieMatch[1]}`; + } + } + + // Now make the actual request with the cookie + const proxyHeaders = getProxyHeaders(event.headers); + + // Add the session cookie if we have one + if (cookieHeader) { + proxyHeaders.set('Cookie', cookieHeader); + } + + // Add additional headers that the browser sends + proxyHeaders.set('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'); + proxyHeaders.set('Accept-Language', 'en-US,en;q=0.9'); + proxyHeaders.set('Accept-Encoding', 'gzip, deflate, br'); + proxyHeaders.set('Connection', 'keep-alive'); + proxyHeaders.set('Sec-Fetch-Dest', 'document'); + proxyHeaders.set('Sec-Fetch-Mode', 'navigate'); + proxyHeaders.set('Sec-Fetch-Site', 'none'); + + // Add certificate headers if available + if (certHeader) { + proxyHeaders.set('X-Certificate', certHeader); + } + if (keyHeader) { + proxyHeaders.set('X-Private-Key', keyHeader); + } + + // Make the actual request + const response = await globalThis.fetch(destination, { + method: event.method, + headers: proxyHeaders, + body: body || undefined, + redirect: 'follow', + credentials: 'include', + }); + + // Get the response data + const responseData = await response.text(); + + // Set response headers + const headers = getAfterResponseHeaders(response.headers, response.url); + setResponseHeaders(event, headers); + + if (token) setTokenHeader(event, token); + + // Return the response + return responseData; + + } catch (e) { + console.log('Error fetching from Febbox', e); + throw e; + } +}); diff --git a/src/utils/headers.ts b/src/utils/headers.ts index c1e3402..a22f533 100644 --- a/src/utils/headers.ts +++ b/src/utils/headers.ts @@ -40,9 +40,18 @@ export function getProxyHeaders(headers: Headers): Headers { // default user agent output.set( 'User-Agent', - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15', ); + // Add common browser headers + output.set('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'); + output.set('Accept-Language', 'en-US,en;q=0.9'); + output.set('Accept-Encoding', 'gzip, deflate, br'); + output.set('Connection', 'keep-alive'); + output.set('Sec-Fetch-Dest', 'document'); + output.set('Sec-Fetch-Mode', 'navigate'); + output.set('Sec-Fetch-Site', 'none'); + Object.entries(headerMap).forEach((entry) => { copyHeader(headers, output, entry[0], entry[1]); });