Feat: proxy

This commit is contained in:
MotorTruck1221 2025-03-23 21:39:32 -06:00
parent a0baded584
commit 2cfdf7fbae
No known key found for this signature in database
GPG key ID: 08F417E2B8B61EA4
10 changed files with 260 additions and 11 deletions

27
public/sw.js Normal file
View file

@ -0,0 +1,27 @@
importScripts(
"/vu/uv.bundle.js",
"/vu/uv.config.js",
"/marcs/scramjet.shared.js",
"/marcs/scramjet.worker.js"
);
importScripts(__uv$config.sw || "/vu/uv.sw.js");
const uv = new UVServiceWorker();
const sj = new ScramjetServiceWorker();
self.addEventListener("fetch", function (event) {
event.respondWith(
(async () => {
await sj.loadConfig();
if (event.request.url.startsWith(location.origin + __uv$config.prefix)) {
return await uv.fetch(event);
}
else if (sj.route(event)) {
return await sj.fetch(event);
}
else {
return await fetch(event.request);
}
})()
);
});

29
public/vu/uv.config.js Normal file
View file

@ -0,0 +1,29 @@
self.__uv$config = {
prefix: "/~/uv/",
encodeUrl: function encode(str) {
if (!str) return str;
return encodeURIComponent(
str
.toString()
.split("")
.map((char, ind) => (ind % 2 ? String.fromCharCode(char.charCodeAt() ^ 3) : char))
.join("")
);
},
decodeUrl: function decode(str) {
if (!str) return str;
let [input, ...search] = str.split("?");
return (
decodeURIComponent(input)
.split("")
.map((char, ind) => (ind % 2 ? String.fromCharCode(char.charCodeAt(0) ^ 3) : char))
.join("") + (search.length ? "?" + search.join("?") : "")
);
},
handler: "/vu/uv.handler.js",
client: "/vu/uv.client.js",
bundle: "/vu/uv.bundle.js",
config: "/vu/uv.config.js",
sw: "/vu/uv.sw.js"
};

View file

@ -1,13 +1,10 @@
[ [
{ {
"splash": "Join to our community! https://discord.gg/qsXnhSPtAK" "splash": "Join our community! https://discord.gg/qsXnhSPtAK"
}, },
{ {
"splash": "Browse safely!" "splash": "Browse safely!"
}, },
{
"splash": "Hello World!"
},
{ {
"splash": "Access with ease!" "splash": "Access with ease!"
}, },

View file

@ -12,6 +12,8 @@ const path = Astro.url.pathname;
<h1 class="text-xl font-bold text-(--foreground)"> Radius </h1> <h1 class="text-xl font-bold text-(--foreground)"> Radius </h1>
</a> </a>
</div> </div>
<div>
</div>
</div> </div>
<div class="fixed w-full h-full z-10 flex flex-row pointer-events-auto invisible transition duration-500" id="navigation"> <div class="fixed w-full h-full z-10 flex flex-row pointer-events-auto invisible transition duration-500" id="navigation">
<div id="innerNav" class="flex flex-col gap-6 w-72 h-full bg-(--background) border-r border-r-(--border) p-6 transition duration-250 shadow-lg"> <div id="innerNav" class="flex flex-col gap-6 w-72 h-full bg-(--background) border-r border-r-(--border) p-6 transition duration-250 shadow-lg">

View file

@ -1,8 +1,12 @@
<script> <script>
import { Settings } from "@utils/settings.ts"; import { Settings } from "@utils/settings.ts";
import { SW } from "@utils/proxy.ts";
const settings = new Settings(); const settings = new Settings();
const sw = new SW();
document.addEventListener('astro:after-swap', async () => { document.addEventListener('astro:after-swap', async () => {
//const settings = await Settings.getInstance();
settings.theme(); settings.theme();
}); });
</script> </script>

45
src/env.d.ts vendored Normal file
View file

@ -0,0 +1,45 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
/// <reference types="@titaniumnetwork-dev/ultraviolet/client" />
interface SJOptions {
prefix: string;
globals?: {
wrapfn: string;
wrapthisfn: string;
trysetfn: string;
importfn: string;
rewritefn: string;
metafn: string;
setrealmfn: string;
pushsourcemapfn: string;
};
files: {
wasm: string;
shared: string;
worker: string;
client: string;
sync: string;
};
flags?: {
serviceworkers?: boolean;
syncxhr?: boolean;
naiiveRewriter?: boolean;
strictRewrites?: boolean;
rewriterLogs?: boolean;
captureErrors?: boolean;
cleanErrors?: boolean;
scramitize?: boolean;
sourcemaps?: boolean;
};
siteFlags?: {};
codec?: {
encode: string;
decode: string;
};
}
declare class ScramjetController {
constructor(opts: SJOptions);
init(): Promise<void>;
encodeUrl(term: string): string;
}

View file

@ -3,13 +3,13 @@ import "@styles/themes/default.css";
import "@styles/global.css"; import "@styles/global.css";
import "@fontsource/inter"; import "@fontsource/inter";
import { ClientRouter } from "astro:transitions"; import { ClientRouter } from "astro:transitions";
import SettingsLoader from "@components/SettingsLoader.astro"; import Loader from "@components/Loader.astro";
import Header from "@components/Header.astro"; import Header from "@components/Header.astro";
--- ---
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<SettingsLoader transition:persist /> <Loader transition:persist />
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />

View file

@ -21,9 +21,35 @@ const randomSplash = genSplash();
</div> </div>
<div class="flex flex-row items-center gap-2 w-4/5 md:w-[26rem] border border-(--input) rounded-lg h-12 p-2"> <div class="flex flex-row items-center gap-2 w-4/5 md:w-[26rem] border border-(--input) rounded-lg h-12 p-2">
<Icon name="lucide:search" /> <Icon name="lucide:search" />
<input type="text" name="Search" class="text-md md:text-sm focus-visible:outline-none w-full h-full placeholder:text-(--muted-foreground)" id="search" placeholder="Search the web"> <input id="input" type="text" name="Search" class="text-md md:text-sm focus-visible:outline-none w-full h-full placeholder:text-(--muted-foreground)" id="search" placeholder="Search the web">
</div> </div>
<p class="text-base"> { randomSplash } </p> <p class="text-sm text-center sm:text-base whitespace-nowrap"> { randomSplash } </p>
</div> </div>
<iframe id="iframe" class="fixed h-[calc(100%-3.5rem)] mt-14 w-full hidden" src="https://example.com" />
</div> </div>
</Layout> </Layout>
<script>
import { SW } from "@utils/proxy.ts";
import { Settings } from "@utils/settings.ts";
const init = async () => {
const input = document.getElementById("input") as HTMLInputElement;
const iframe = document.getElementById("iframe") as HTMLIFrameElement;
input.addEventListener("keypress", async (event: any) => {
if (event.key === "Enter") {
const sw = SW.getInstance().next().value!;
const settings = await Settings.getInstance();
await sw.setTransport();
iframe.classList.remove("hidden");
iframe.src = sw.encodeURL(input.value, 'uv');
}
});
}
document.addEventListener("astro:page-load", async () => {
try {
await init();
}
catch (_) {}
})
</script>

119
src/utils/proxy.ts Normal file
View file

@ -0,0 +1,119 @@
import { BareMuxConnection } from "@mercuryworkshop/bare-mux";
import { StoreManager } from "./storage";
const createScript = (src: string, defer?: boolean) => {
const script = document.createElement('script') as HTMLScriptElement;
script.src = src;
if (defer) script.defer = defer;
return document.body.appendChild(script);
}
/**
* This class automatically sets up and handles lots of stuff for us.
*
* It registers/fixes errors with SW reg
* It creates our bareMux worker
* And other stuff.
*
* @example
* import { SW } from "@utils/proxy.ts";
* const handler = new SW();
* //Consume the methods
* // Or if an instance is already running
* import { SW } from "@utils/proxy.ts";
* const handler = SW.getInstance();
* //Consume the methods
*/
class SW {
#baremuxConn?: BareMuxConnection;
#scramjetController?: ScramjetController;
#serviceWorker?: ServiceWorkerRegistration;
#storageManager: StoreManager<"radius||settings">;
static #instance = new Set();
static *getInstance() {
for (const val of SW.#instance.keys()) {
yield val as SW;
}
}
#search(input: string, template: string) {
try { return new URL(input).toString() } catch (_) {};
try {
const url = new URL(`http://${input}`);
if (url.hostname.includes(".")) return url.toString();
} catch (_) {};
return template.replace("%s", encodeURIComponent(input));
}
encodeURL(string: string, proxy: 'uv' | 'scram'): string {
const input = this.#search(string, "https://google.com/search?q=%s");
return proxy === 'uv' ? `${__uv$config.prefix}${__uv$config.encodeUrl!(input)}` : this.#scramjetController!.encodeUrl(input)
}
async setTransport(transport?: 'epoxy' | 'libcurl') {
this.#storageManager.setVal("transport", transport || this.#storageManager.getVal("transport") || 'epoxy');
switch(transport) {
case 'epoxy': {
await this.#baremuxConn!.setTransport("/epoxy/index.mjs", [ { wisp: 'ws://localhost:4321/wisp/' }]);
}
case 'libcurl': {
await this.#baremuxConn!.setTransport("/libcurl/index.mjs", [ { wisp: 'ws://localhost:4321/wisp/' }]);
}
default: {
await this.#baremuxConn!.setTransport("/epoxy/index.mjs", [ { wisp: 'ws://localhost:4321/wisp/' }]);
}
}
}
constructor() {
SW.#instance.add(this);
this.#storageManager = new StoreManager("radius||settings");
const checkScripts = (): Promise<void> => {
return new Promise((resolve) => {
const t = setInterval(() => {
if (typeof __uv$config !== 'undefined' && typeof ScramjetController !== 'undefined') {
clearInterval(t);
resolve();
}
});
});
};
createScript('/vu/uv.bundle.js', true);
createScript('/vu/uv.config.js', true);
createScript('/marcs/scramjet.controller.js', true);
checkScripts().then(async () => {
this.#baremuxConn = new BareMuxConnection("/erab/worker.js");
await this.setTransport();
this.#scramjetController = new ScramjetController({
prefix: '/~/scramjet/',
files: {
wasm: "/marcs/scramjet.wasm.wasm",
worker: "/marcs/scramjet.worker.js",
client: "/marcs/scramjet.client.js",
shared: "/marcs/scramjet.shared.js",
sync: "/marcs/scramjet.sync.js"
},
flags: {
rewriterLogs: false
}
});
if ("serviceWorker" in navigator) {
await this.#scramjetController.init();
navigator.serviceWorker.ready.then(async (reg) => {
console.log('SW ready to go!');
this.#serviceWorker = reg;
});
navigator.serviceWorker.register("/sw.js", { scope: '/' });
}
else {
throw new Error('Your browser is not supported! This website uses Service Workers heavily.');
}
});
};
}
export { SW };

View file

@ -1,5 +1,6 @@
import { StoreManager } from "./storage"; import { StoreManager } from "./storage";
import { BareMuxConnection } from "@mercuryworkshop/bare-mux";
import { SW } from "@utils/proxy.ts";
/** /**
* The settings class * The settings class
* Initializes it's own StorageManager, and handles everything within the class itself * Initializes it's own StorageManager, and handles everything within the class itself
@ -22,7 +23,6 @@ class Settings {
#storageManager: StoreManager<"radius||settings">; #storageManager: StoreManager<"radius||settings">;
static #instance = new Set(); static #instance = new Set();
/** /**
* Method to get the current or other Settings instance(s) * Method to get the current or other Settings instance(s)
* *