Implemented backend proxy
This commit is contained in:
+20
-10
@@ -1,8 +1,12 @@
|
||||
import { timeout } from '$lib/misc';
|
||||
import { isUnrestrictedPlatform, timeout } from '$lib/misc';
|
||||
import { Capacitor } from '@capacitor/core';
|
||||
|
||||
const originalFetch = window.fetch;
|
||||
const corsProxyUrl: string = 'http://localhost:3000/';
|
||||
const corsProxyUrl =
|
||||
Capacitor.getPlatform() === 'android'
|
||||
? 'http://localhost:3000/'
|
||||
: `${location.origin}/api/proxy/`;
|
||||
|
||||
let mobileNodeLoaded = false;
|
||||
|
||||
function needsProxying(target: string): boolean {
|
||||
@@ -10,11 +14,11 @@ function needsProxying(target: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
export const androidFetch = async (
|
||||
export const fetchProxied = async (
|
||||
requestInput: string | URL | Request,
|
||||
requestOptions?: RequestInit
|
||||
): Promise<Response> => {
|
||||
if (!mobileNodeLoaded) {
|
||||
if (!mobileNodeLoaded && Capacitor.getPlatform() === 'android') {
|
||||
let mobileNodeResp: Response | undefined;
|
||||
|
||||
while (!mobileNodeResp || !mobileNodeResp.ok) {
|
||||
@@ -29,11 +33,17 @@ export const androidFetch = async (
|
||||
mobileNodeLoaded = true;
|
||||
}
|
||||
|
||||
const uri = requestInput instanceof Request ? requestInput.url : requestInput.toString();
|
||||
const nonProxiedUrl =
|
||||
requestInput instanceof Request ? requestInput.url : requestInput.toString();
|
||||
|
||||
if (needsProxying(nonProxiedUrl)) {
|
||||
const proxiedURLEncoded =
|
||||
Capacitor.getPlatform() === 'android' ? nonProxiedUrl : encodeURIComponent(nonProxiedUrl);
|
||||
|
||||
const proxiedUrl = corsProxyUrl + proxiedURLEncoded;
|
||||
|
||||
if (needsProxying(uri)) {
|
||||
if (requestInput instanceof Request) {
|
||||
requestInput = new Request(corsProxyUrl + uri, {
|
||||
requestInput = new Request(proxiedUrl, {
|
||||
method: requestInput.method,
|
||||
headers: requestInput.headers,
|
||||
body: requestInput.body,
|
||||
@@ -47,7 +57,7 @@ export const androidFetch = async (
|
||||
...(requestInput.body ? { duplex: 'half' } : {})
|
||||
});
|
||||
} else {
|
||||
requestInput = corsProxyUrl + uri;
|
||||
requestInput = proxiedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,8 +65,8 @@ export const androidFetch = async (
|
||||
return originalFetch(requestInput, requestOptions);
|
||||
};
|
||||
|
||||
if (Capacitor.getPlatform() === 'android') {
|
||||
window.fetch = androidFetch;
|
||||
if (isUnrestrictedPlatform() && Capacitor.getPlatform() !== 'electron') {
|
||||
window.fetch = fetchProxied;
|
||||
|
||||
const originalXhrOpen = XMLHttpRequest.prototype.open;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { isAndroidTvStore } from '$lib/store';
|
||||
import { App } from '@capacitor/app';
|
||||
import '$lib/android/http/androidRequests';
|
||||
import '$lib/fetchProxy';
|
||||
|
||||
let { children } = $props();
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
const ALLOWED_HEADERS = [
|
||||
'Origin',
|
||||
'X-Requested-With',
|
||||
'Content-Type',
|
||||
'Accept',
|
||||
'Authorization',
|
||||
'x-goog-visitor-id',
|
||||
'x-goog-api-key',
|
||||
'x-origin',
|
||||
'x-youtube-client-version',
|
||||
'x-youtube-client-name',
|
||||
'x-goog-api-format-version',
|
||||
'x-goog-authuser',
|
||||
'x-user-agent',
|
||||
'Accept-Language',
|
||||
'X-Goog-FieldMask',
|
||||
'Range',
|
||||
'Referer',
|
||||
'Cookie'
|
||||
].join(', ');
|
||||
|
||||
function copyHeader(header: string, targetHeaders: Headers, sourceHeaders: Headers) {
|
||||
if (sourceHeaders.has(header)) {
|
||||
targetHeaders.set(header, sourceHeaders.get(header)!);
|
||||
}
|
||||
}
|
||||
|
||||
async function proxyRequest(request: Request, urlToProxy: string): Promise<Response> {
|
||||
let urlToProxyObj: URL;
|
||||
try {
|
||||
urlToProxyObj = new URL(decodeURIComponent(urlToProxy));
|
||||
} catch {
|
||||
return new Response('Invalid URL', { status: 400 });
|
||||
}
|
||||
|
||||
const requestHeaders = request.headers;
|
||||
requestHeaders.set('host', urlToProxyObj.host);
|
||||
requestHeaders.set('origin', urlToProxyObj.origin);
|
||||
|
||||
for (const key of [
|
||||
'referer',
|
||||
'x-forwarded-for',
|
||||
'x-requested-with',
|
||||
'sec-ch-ua-mobile',
|
||||
'sec-ch-ua',
|
||||
'sec-ch-ua-platform'
|
||||
]) {
|
||||
requestHeaders.delete(key);
|
||||
}
|
||||
|
||||
const fetchRes = await fetch(urlToProxy.toString(), {
|
||||
method: request.method,
|
||||
headers: requestHeaders,
|
||||
body: request.body,
|
||||
credentials: 'same-origin',
|
||||
...(request.body ? { duplex: 'half' } : {})
|
||||
});
|
||||
|
||||
const responseHeaders = new Headers();
|
||||
copyHeader('content-length', requestHeaders, fetchRes.headers);
|
||||
copyHeader('content-type', requestHeaders, fetchRes.headers);
|
||||
copyHeader('content-disposition', requestHeaders, fetchRes.headers);
|
||||
copyHeader('accept-ranges', requestHeaders, fetchRes.headers);
|
||||
copyHeader('content-range', requestHeaders, fetchRes.headers);
|
||||
|
||||
return new Response(fetchRes.body, {
|
||||
status: fetchRes.status,
|
||||
headers: responseHeaders
|
||||
});
|
||||
}
|
||||
|
||||
export async function GET({ request, params }) {
|
||||
return await proxyRequest(request, params.urlToProxy);
|
||||
}
|
||||
export async function PATCH({ request, params }) {
|
||||
return await proxyRequest(request, params.urlToProxy);
|
||||
}
|
||||
|
||||
export async function DELETE({ request, params }) {
|
||||
return await proxyRequest(request, params.urlToProxy);
|
||||
}
|
||||
|
||||
export async function PUT({ request, params }) {
|
||||
return await proxyRequest(request, params.urlToProxy);
|
||||
}
|
||||
|
||||
export async function POST({ request, params }) {
|
||||
return await proxyRequest(request, params.urlToProxy);
|
||||
}
|
||||
|
||||
export async function OPTIONS({ request }) {
|
||||
return new Response('', {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': request.headers.get('origin') || '',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': ALLOWED_HEADERS,
|
||||
'Access-Control-Max-Age': '86400',
|
||||
'Access-Control-Allow-Credentials': 'true'
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user