Merge pull request #223 from MotorTruck1221/wisp

TLS
This commit is contained in:
rift 2024-03-07 12:18:28 -06:00 committed by GitHub
commit 6c1ce96036
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 2033 additions and 1809 deletions

View file

@ -1,40 +1,40 @@
<!doctype html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html {
color-scheme: light dark;
}
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required. If you are expecting another
page, please check your network or
<a href="/" id="rcheck"><b>Refresh this page</b></a>
</p>
<p>
For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br />
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.
</p>
<p><em>Thank you for using nginx.</em></p>
<script>
if (!localStorage["auth"] && new URL(document.all.rcheck.href).password) {
window.location.reload();
localStorage["auth"] = 1;
}
</script>
</body>
</html>
<!doctype html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html {
color-scheme: light dark;
}
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required. If you are expecting another
page, please check your network or
<a href="/" id="rcheck"><b>Refresh this page</b></a>
</p>
<p>
For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br />
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.
</p>
<p><em>Thank you for using nginx.</em></p>
<script>
if (!localStorage["auth"] && new URL(document.all.rcheck.href).password) {
window.location.reload();
localStorage["auth"] = 1;
}
</script>
</body>
</html>

View file

@ -3,11 +3,12 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="/epoxy/index.js" defer></script>
<script src="/uv/uv.bundle.js" defer></script>
<script src="/uv/uv.config.js" defer></script>
<script src="/dynamic/dynamic.config.js" defer></script>
<script src="/localforage/localforage.min.js" defer></script>
<script>
<script defer>
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js", {
@ -27,11 +28,13 @@
)
window.location.href = window.location.href;
</script>
<!-- ONLY ENABLE IF USING MASQR!
<script>
if (!localStorage["auth"] && new URL(document.all.rcheck.href).password) {
window.location.reload();
localStorage["auth"] = 1;
}
</script>
-->
</body>
</html>

View file

@ -1,58 +1,62 @@
{
"name": "nebula",
"private": true,
"type": "module",
"scripts": {
"dev": "concurrently \"vite\" \"bare-server-node --port 8080\"",
"build": "vite build",
"bstart": "npm run build && tsx server.ts",
"start": "tsx server.ts",
"preview": "vite preview",
"format": "prettier --write ."
},
"dependencies": {
"@fastify/compress": "^6.5.0",
"@fastify/cookie": "^9.3.1",
"@fastify/static": "^6.12.0",
"@nebula-services/bare-server-node": "2.0.1-patch.1",
"@nebula-services/dynamic": "0.7.2-patch.2",
"@nebula-services/ultraviolet": "1.0.1-1.patch.7",
"chalk": "^5.3.0",
"classnames": "^2.3.2",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"crypto-js": "^4.2.0",
"express": "^4.18.2",
"fastify": "^4.25.1",
"framer-motion": "^10.16.16",
"i18next": "^23.7.9",
"i18next-browser-languagedetector": "^7.2.0",
"localforage": "^1.10.0",
"million": "^2.6.4",
"preact": "^10.13.1",
"preact-iso": "^2.3.2",
"preact-render-to-string": "^6.3.1",
"preact-router": "^4.1.2",
"rammerhead": "https://github.com/NebulaServices/rammerhead/releases/download/rammerhead-1.2.41-nebula.8/rammerhead-1.2.41-nebula.7.tgz",
"react-helmet": "^6.1.0",
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
"react-toastify": "^9.1.3",
"tsx": "^4.7.0",
"wisp-server-node": "^1.0.1"
},
"devDependencies": {
"@preact/preset-vite": "^2.5.0",
"autoprefixer": "^10.4.16",
"concurrently": "^8.2.2",
"eslint": "^8.55.0",
"eslint-config-preact": "^1.3.0",
"postcss": "^8.4.32",
"prettier": "^3.1.1",
"prettier-plugin-tailwindcss": "^0.5.9",
"tailwindcss": "^3.3.6",
"typescript": "^5.3.3",
"vite": "^5.0.9",
"vite-plugin-static-copy": "^1.0.0"
}
}
{
"name": "nebula",
"private": true,
"type": "module",
"scripts": {
"dev": "concurrently \"vite\" \"bare-server-node --port 8080\"",
"build": "vite build",
"bstart": "npm run build && tsx server.ts",
"start": "tsx server.ts",
"preview": "vite preview",
"format": "prettier --write ."
},
"dependencies": {
"@fastify/compress": "^6.5.0",
"@fastify/cookie": "^9.3.1",
"@fastify/static": "^6.12.0",
"@mercuryworkshop/bare-mux": "^1.0.4",
"@mercuryworkshop/epoxy-transport": "^1.0.2",
"@nebula-services/bare-server-node": "2.0.1-patch.1",
"@nebula-services/dynamic": "0.7.2-patch.2",
"@titaniumnetwork-dev/ultraviolet": "^3.0.0",
"@types/express": "^4.17.21",
"chalk": "^5.3.0",
"classnames": "^2.5.1",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"crypto-js": "^4.2.0",
"express": "^4.18.3",
"fastify": "^4.26.2",
"framer-motion": "^10.18.0",
"i18next": "^23.10.0",
"i18next-browser-languagedetector": "^7.2.0",
"localforage": "^1.10.0",
"million": "^2.6.4",
"preact": "^10.19.6",
"preact-iso": "^2.4.0",
"preact-render-to-string": "^6.4.0",
"preact-router": "^4.1.2",
"rammerhead": "https://github.com/NebulaServices/rammerhead/releases/download/rammerhead-1.2.41-nebula.8/rammerhead-1.2.41-nebula.7.tgz",
"react-helmet": "^6.1.0",
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
"react-toastify": "^9.1.3",
"tsx": "^4.7.1",
"wisp-server-node": "^1.0.1",
"ws": "^8.16.0"
},
"devDependencies": {
"@preact/preset-vite": "^2.8.1",
"autoprefixer": "^10.4.18",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"eslint-config-preact": "^1.3.0",
"postcss": "^8.4.35",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"vite-plugin-static-copy": "^1.0.1"
}
}

2816
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
importScripts("/epoxy/index.js");
importScripts("/uv/uv.bundle.js");
importScripts("/uv/uv.config.js");
importScripts(__uv$config.sw || "/uv/uv.sw.js");

View file

@ -9,7 +9,8 @@ import path from "path";
import fs from "fs";
import cookieParser from "cookie-parser";
import wisp from "wisp-server-node";
import { Socket } from "net";
import { Request, Response } from "express";
import { Socket, Head } from "ws";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@ -47,7 +48,7 @@ app.use(
app.use(cookieParser());
// Congratulations! Masqr failed to validate, this is either your first visit or you're a FRAUD
async function MasqFail(req, res) {
async function MasqFail(req: Request, res: Response) {
if (!req.headers.host) {
// no bitch still using HTTP/1.0 go away
return;
@ -124,7 +125,7 @@ async function MasqFail(req, res) {
app.use(express.static("dist"));
app.get("/search=:query", async (req, res) => {
app.get("/search=:query", async (req: Request, res: Response) => {
const { query } = req.params;
const response = await fetch(
@ -142,7 +143,7 @@ const server = createServer();
const bare = createBareServer("/bare/");
server.on("request", (req, res) => {
server.on("request", (req: Request, res: Response) => {
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
} else if (shouldRouteRh(req)) {
@ -152,13 +153,13 @@ server.on("request", (req, res) => {
}
});
server.on("upgrade", (req, socket, head) => {
server.on("upgrade", (req: Request, socket: Socket, head: Head) => {
if (bare.shouldRoute(req)) {
bare.routeUpgrade(req, socket, head);
} else if (shouldRouteRh(req)) {
routeRhUpgrade(req, socket, head);
} else {
wisp.routeRequest(req, socket as Socket, head);
} else if (req.url.endsWith("/wisp/")) {
wisp.routeRequest(req, socket, head);
}
});
@ -170,11 +171,11 @@ function shouldRouteRh(req) {
);
}
function routeRhRequest(req, res) {
function routeRhRequest(req: Request, res: Response) {
rh.emit("request", req, res);
}
function routeRhUpgrade(req, socket, head) {
function routeRhUpgrade(req: Request, socket: Socket, head: Head) {
rh.emit("upgrade", req, socket, head);
}

View file

@ -1,88 +1,88 @@
import { Helmet } from "react-helmet";
export default function Meta() {
return (
<Helmet>
<meta name="googlebot" content="index, follow, snippet" />
<link rel="canonical" href="https://nebulaproxy.io/" />
<meta
name="keywords"
content="proxy, web proxy, unblock websites, unblock chromebook, free web proxy, proxy list, proxy sites, un block chromebook, online proxy, proxy server, proxysite, proxy youtube, bypass securly, bypass iboss, bypass lightspeed filter, chromebooks, titanium network, unblock youtube, youtube proxy, unblocked youtube, youtube unblocked"
/>
<meta
name="description"
content="NebulaWeb is an official flagship of Nebula Services and Nebula Developer Labs. NebulaWeb is a stunning, sleek, and functional web-proxy with support for thousands of popular sites. With NebulaWeb, the sky is the limit."
/>
<meta property="og:site_name" content="Nebula" />
<meta property="og:url" content="https://nebulaproxy.io/" />
<meta property="og:title" content="Nebula" />
<meta property="og:image" content="/logo.png" />
<meta property="og:image:secure_url" content="/logo.png" />
<meta property="og:type" content="website" />
<meta name="color-scheme" content="light dark" />
<meta property="og:title" content="Nebula" />
<meta
content="A stunning and sleak web proxy frontend with support for hundreds of popular sites."
property="og:description"
/>
<meta name="theme-color" content="#191724" />
<script type="application/ld+json">
{`
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Nebula",
"url": "https://nebulaproxy.io",
"sameAs": [
"https://github.com/NebulaServices",
"https://nebulaproxy.io"
]
}
`}
</script>
<script type="application/ld+json">
{`
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [{
"@type": "Question",
"name": "How do I get more links?",
"acceptedAnswer": {
"@type": "Answer",
"text": "You can more links by joining our discord server at discord.gg/unblocker"
}
}, {
"@type": "Question",
"name": "How can I self host Nebula?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Instructions for self hosting Nebula can be found at our GitHub repository."
}
}, {
"@type": "Question",
"name": "What sites can I unblock with Nebula?",
"acceptedAnswer": {
"@type": "Answer",
"text": "With Nebula you can access sites such as Discord, Spotify, YouTube and other game sites!"
}
}, {
"@type": "Question",
"name": "Does Nebula hide my search history?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes! Change your Tab appearance in Settings and make the tab look like another site!."
}
}, {
"@type": "Question",
"name": "Is Nebula open-source?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes! Check out our GitHub where you can deploy or host your own instance of Nebula."
}
}]
}
`}
</script>
</Helmet>
);
}
import { Helmet } from "react-helmet";
export default function Meta() {
return (
<Helmet>
<meta name="googlebot" content="index, follow, snippet" />
<link rel="canonical" href="https://nebulaproxy.io/" />
<meta
name="keywords"
content="proxy, web proxy, unblock websites, unblock chromebook, free web proxy, proxy list, proxy sites, un block chromebook, online proxy, proxy server, proxysite, proxy youtube, bypass securly, bypass iboss, bypass lightspeed filter, chromebooks, titanium network, unblock youtube, youtube proxy, unblocked youtube, youtube unblocked"
/>
<meta
name="description"
content="NebulaWeb is an official flagship of Nebula Services and Nebula Developer Labs. NebulaWeb is a stunning, sleek, and functional web-proxy with support for thousands of popular sites. With NebulaWeb, the sky is the limit."
/>
<meta property="og:site_name" content="Nebula" />
<meta property="og:url" content="https://nebulaproxy.io/" />
<meta property="og:title" content="Nebula" />
<meta property="og:image" content="/logo.png" />
<meta property="og:image:secure_url" content="/logo.png" />
<meta property="og:type" content="website" />
<meta name="color-scheme" content="light dark" />
<meta property="og:title" content="Nebula" />
<meta
content="A stunning and sleak web proxy frontend with support for hundreds of popular sites."
property="og:description"
/>
<meta name="theme-color" content="#191724" />
<script type="application/ld+json">
{`
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Nebula",
"url": "https://nebulaproxy.io",
"sameAs": [
"https://github.com/NebulaServices",
"https://nebulaproxy.io"
]
}
`}
</script>
<script type="application/ld+json">
{`
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [{
"@type": "Question",
"name": "How do I get more links?",
"acceptedAnswer": {
"@type": "Answer",
"text": "You can more links by joining our discord server at discord.gg/unblocker"
}
}, {
"@type": "Question",
"name": "How can I self host Nebula?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Instructions for self hosting Nebula can be found at our GitHub repository."
}
}, {
"@type": "Question",
"name": "What sites can I unblock with Nebula?",
"acceptedAnswer": {
"@type": "Answer",
"text": "With Nebula you can access sites such as Discord, Spotify, YouTube and other game sites!"
}
}, {
"@type": "Question",
"name": "Does Nebula hide my search history?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes! Change your Tab appearance in Settings and make the tab look like another site!."
}
}, {
"@type": "Question",
"name": "Is Nebula open-source?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes! Check out our GitHub where you can deploy or host your own instance of Nebula."
}
}]
}
`}
</script>
</Helmet>
);
}

View file

@ -1,4 +1,4 @@
.highlighted {
background-color: var(--navbar-text-color);
transition: background-color 1s ease;
}
.highlighted {
background-color: var(--navbar-text-color);
transition: background-color 1s ease;
}

View file

@ -1,51 +1,51 @@
import { useTranslation } from "react-i18next";
import { HeaderRoute } from "../components/HeaderRoute";
import CloakedHead from "../util/CloakedHead";
import { useEffect } from "preact/hooks";
import "./Faq.css";
export function Faq() {
const { t } = useTranslation();
const faqData = t("faq", { returnObjects: true });
useEffect(() => {
const hash = window.location.hash.substring(1);
if (hash) {
const highlightedDiv = document.getElementById(hash);
if (highlightedDiv) {
highlightedDiv.classList.add("highlighted");
}
}
}, []);
return (
<HeaderRoute>
<CloakedHead
originalTitle={t("titles.discord")}
originalFavicon="/logo.png"
/>
<div class="p-10">
{Object.values(faqData).map((item, index) => (
<div key={index} className="py-3">
<p className="text-4xl" id={(index + 1).toString()}>
{item.q}
</p>
<div class="flex flex-row">
<p className="text-lg">
{item.a}{" "}
{item.h && (
<a href={item.hR} class="underline">
{item.h}
</a>
)}
</p>
</div>
</div>
))}
</div>
</HeaderRoute>
);
}
import { useTranslation } from "react-i18next";
import { HeaderRoute } from "../components/HeaderRoute";
import CloakedHead from "../util/CloakedHead";
import { useEffect } from "preact/hooks";
import "./Faq.css";
export function Faq() {
const { t } = useTranslation();
const faqData = t("faq", { returnObjects: true });
useEffect(() => {
const hash = window.location.hash.substring(1);
if (hash) {
const highlightedDiv = document.getElementById(hash);
if (highlightedDiv) {
highlightedDiv.classList.add("highlighted");
}
}
}, []);
return (
<HeaderRoute>
<CloakedHead
originalTitle={t("titles.discord")}
originalFavicon="/logo.png"
/>
<div class="p-10">
{Object.values(faqData).map((item, index) => (
<div key={index} className="py-3">
<p className="text-4xl" id={(index + 1).toString()}>
{item.q}
</p>
<div class="flex flex-row">
<p className="text-lg">
{item.a}{" "}
{item.h && (
<a href={item.hR} class="underline">
{item.h}
</a>
)}
</p>
</div>
</div>
))}
</div>
</HeaderRoute>
);
}

View file

@ -1,29 +1,29 @@
export function BareTest(bareUrl) {
const headers = new Headers({
"x-bare-url": "https://www.google.com",
"X-Bare-Headers": JSON.stringify({
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
})
});
return fetch(bareUrl, {
method: "GET",
headers: headers
})
.then((response) => {
if (
response.headers.get("x-bare-status") === "200" ||
response.headers.get("x-bare-status") === "302"
) {
return true;
} else {
// the site is a real site but doesn't act like a bare server
return false;
}
})
.catch((error) => {
// incase the site doesn't exist
return false;
});
}
export function BareTest(bareUrl) {
const headers = new Headers({
"x-bare-url": "https://www.google.com",
"X-Bare-Headers": JSON.stringify({
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
})
});
return fetch(bareUrl, {
method: "GET",
headers: headers
})
.then((response) => {
if (
response.headers.get("x-bare-status") === "200" ||
response.headers.get("x-bare-status") === "302"
) {
return true;
} else {
// the site is a real site but doesn't act like a bare server
return false;
}
})
.catch((error) => {
// incase the site doesn't exist
return false;
});
}

View file

@ -2,7 +2,9 @@ import { motion } from "framer-motion";
import { tabContentVariant, settingsPageVariant } from "./Variants";
import Dropdown from "./Dropdown";
import BareInput from "./BareInput";
import WispInput from "./WispInput";
import ProxyInput from "./ProxyInput";
import { changeTransport } from "../../util/transports";
import { useTranslation } from "react-i18next";
const Proxy = ({ id, active }) => {
@ -27,6 +29,13 @@ const Proxy = ({ id, active }) => {
{ id: "https://bing.com/search?q=%s", label: "Bing" }
];
const wispUrl =
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/";
//libcurl can be added here when it's stable
const transports = [{ id: "epoxy", label: "Epoxy" }];
return (
<motion.div
role="tabpanel"
@ -84,6 +93,29 @@ const Proxy = ({ id, active }) => {
</div>
<BareInput placeholder="/bare/" storageKey="bare" />
</div>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
Wisp Server
</div>
<div className="text-md p-4 font-bold text-input-text">
Enter the url of a Wisp server
</div>
<WispInput placeholder={wispUrl} />
</div>
<div className="flex h-64 w-80 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
Transport
</div>
<div className="text-md p-4 font-bold text-input-text">
Select the transport to use
</div>
<Dropdown
storageKey="transport"
options={transports}
refresh={false}
onChange={(value) => changeTransport(value, wispUrl)}
/>
</div>
<div className="flex h-96 w-96 flex-col flex-wrap content-center items-center rounded-lg border border-input-border-color bg-lighter p-2 text-center">
<div className="p-2 text-3xl font-bold text-input-text">
{t("settings.httpProxy.title")}

View file

@ -1,140 +1,140 @@
import { useState, useEffect } from "preact/hooks";
import { set } from "../../util/IDB";
import { uninstallServiceWorkers } from "../../util/SWHelper";
import { useTranslation } from "react-i18next";
import { ToastContainer, toast } from "react-toastify";
interface BareInputProps {
placeholder: string;
storageKey: string;
}
function ProxyInput(props: BareInputProps) {
const { t } = useTranslation();
const bareServer = localStorage.getItem("bare") || "/bare/";
const HTTPProxy = localStorage.getItem("HTTPProxy") || "";
const [inputValue, setInputValue] = useState(HTTPProxy);
function resetProxy() {
set("HTTPProxy", "");
localStorage.setItem("HTTPProxy", "");
uninstallServiceWorkers();
window.location.reload();
}
function validateUrl(url: string) {
let finalUrl = url;
if (url === null || url === undefined || url === "") {
finalUrl = "";
return finalUrl;
}
return finalUrl;
}
function handleChange() {
const proxyUrl = validateUrl(
(document.getElementById("pinput") as HTMLInputElement).value
);
if (!(proxyUrl === "")) {
const [proxyIP, proxyPort] = proxyUrl.split(":");
fetch(bareServer)
.then((response) => response.json())
.then((jsonResponse) => {
if (jsonResponse.hasOwnProperty("HTTPProxy")) {
const headers = new Headers({
"x-bare-url": "https://www.google.com",
"X-Bare-Headers": JSON.stringify({
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
}),
"x-bare-proxy-ip": proxyIP,
"x-bare-proxy-port": proxyPort
});
return fetch(bareServer + "v3/", {
method: "GET",
headers: headers
})
.then((response) => {
if (
response.headers.get("x-bare-status") === "200" ||
response.headers.get("x-bare-status") === "302"
) {
// Success!
set("HTTPProxy", proxyUrl);
localStorage.setItem("HTTPProxy", proxyUrl);
uninstallServiceWorkers();
window.location.reload();
return true;
} else {
(
document.getElementById("pinput") as HTMLInputElement
).value = localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badProxy"), {
type: "error"
});
}
})
.catch((error) => {
(document.getElementById("pinput") as HTMLInputElement).value =
localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badProxy"), {
type: "error"
});
});
} else {
(document.getElementById("pinput") as HTMLInputElement).value =
localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badBare"), {
type: "error"
});
}
})
.catch((error) => console.error("Error:", error));
} else {
// reset UV config to have no proxy
set("HTTPProxy", "");
localStorage.setItem("HTTPProxy", "");
uninstallServiceWorkers();
window.location.reload();
}
}
return (
<div>
<ToastContainer position="bottom-right" theme="dark" />
<div className="flex flex-col items-center">
<input
type="text"
placeholder={props.placeholder}
value={inputValue}
onKeyPress={(event) => {
if (event.key === "Enter") {
handleChange();
}
}}
id="pinput"
className="font-roboto flex h-14 w-56 flex-row rounded-2xl border border-input-border-color bg-input p-4 text-center text-sm text-input-text"
/>
<div class="flex flex-row gap-4">
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={handleChange}
>
{t("settings.bare.select")}
</div>
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={resetProxy}
>
{t("settings.httpProxy.reset")}
</div>
</div>
</div>
</div>
);
}
export default ProxyInput;
import { useState, useEffect } from "preact/hooks";
import { set } from "../../util/IDB";
import { uninstallServiceWorkers } from "../../util/SWHelper";
import { useTranslation } from "react-i18next";
import { ToastContainer, toast } from "react-toastify";
interface BareInputProps {
placeholder: string;
storageKey: string;
}
function ProxyInput(props: BareInputProps) {
const { t } = useTranslation();
const bareServer = localStorage.getItem("bare") || "/bare/";
const HTTPProxy = localStorage.getItem("HTTPProxy") || "";
const [inputValue, setInputValue] = useState(HTTPProxy);
function resetProxy() {
set("HTTPProxy", "");
localStorage.setItem("HTTPProxy", "");
uninstallServiceWorkers();
window.location.reload();
}
function validateUrl(url: string) {
let finalUrl = url;
if (url === null || url === undefined || url === "") {
finalUrl = "";
return finalUrl;
}
return finalUrl;
}
function handleChange() {
const proxyUrl = validateUrl(
(document.getElementById("pinput") as HTMLInputElement).value
);
if (!(proxyUrl === "")) {
const [proxyIP, proxyPort] = proxyUrl.split(":");
fetch(bareServer)
.then((response) => response.json())
.then((jsonResponse) => {
if (jsonResponse.hasOwnProperty("HTTPProxy")) {
const headers = new Headers({
"x-bare-url": "https://www.google.com",
"X-Bare-Headers": JSON.stringify({
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
}),
"x-bare-proxy-ip": proxyIP,
"x-bare-proxy-port": proxyPort
});
return fetch(bareServer + "v3/", {
method: "GET",
headers: headers
})
.then((response) => {
if (
response.headers.get("x-bare-status") === "200" ||
response.headers.get("x-bare-status") === "302"
) {
// Success!
set("HTTPProxy", proxyUrl);
localStorage.setItem("HTTPProxy", proxyUrl);
uninstallServiceWorkers();
window.location.reload();
return true;
} else {
(
document.getElementById("pinput") as HTMLInputElement
).value = localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badProxy"), {
type: "error"
});
}
})
.catch((error) => {
(document.getElementById("pinput") as HTMLInputElement).value =
localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badProxy"), {
type: "error"
});
});
} else {
(document.getElementById("pinput") as HTMLInputElement).value =
localStorage.getItem("HTTPProxy") || "";
toast(t("settings.httpProxy.badBare"), {
type: "error"
});
}
})
.catch((error) => console.error("Error:", error));
} else {
// reset UV config to have no proxy
set("HTTPProxy", "");
localStorage.setItem("HTTPProxy", "");
uninstallServiceWorkers();
window.location.reload();
}
}
return (
<div>
<ToastContainer position="bottom-right" theme="dark" />
<div className="flex flex-col items-center">
<input
type="text"
placeholder={props.placeholder}
value={inputValue}
onKeyPress={(event) => {
if (event.key === "Enter") {
handleChange();
}
}}
id="pinput"
className="font-roboto flex h-14 w-56 flex-row rounded-2xl border border-input-border-color bg-input p-4 text-center text-sm text-input-text"
/>
<div class="flex flex-row gap-4">
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={handleChange}
>
{t("settings.bare.select")}
</div>
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={resetProxy}
>
{t("settings.httpProxy.reset")}
</div>
</div>
</div>
</div>
);
}
export default ProxyInput;

View file

@ -0,0 +1,66 @@
import { useState, useEffect } from "preact/hooks";
import { useTranslation } from "react-i18next";
import { changeTransport } from "../../util/transports";
import { ToastContainer, toast } from "react-toastify";
interface WispInputProps {
placeholder: string;
}
function WispInput(props: WispInputProps) {
const { t } = useTranslation();
const value =
localStorage.getItem("wispUrl") ||
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/";
const [inputValue, setInputValue] = useState(value);
function validateUrl(url: string) {
let finalUrl = url;
if (finalUrl.startsWith("http://")) {
finalUrl = finalUrl.replace("http://", "ws://");
} else if (finalUrl.startsWith("https://")) {
finalUrl = finalUrl.replace("https://", "wss://");
} else if (finalUrl === "" || finalUrl === null || finalUrl === undefined) {
finalUrl =
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/";
}
return finalUrl;
}
function handleChange() {
const url = validateUrl(
(document.getElementById("wispinput") as HTMLInputElement).value
);
localStorage.setItem("wispUrl", url);
changeTransport(localStorage.getItem("transport") || "epoxy", url);
}
return (
<div>
<ToastContainer position="bottom-right" theme="dark" />
<div className="flex flex-col items-center">
<input
type="text"
placeholder={props.placeholder}
value={inputValue}
onKeyPress={(event) => {
if (event.key === "Enter") {
handleChange();
}
}}
id="wispinput"
className="font-roboto flex h-14 w-56 flex-row rounded-2xl border border-input-border-color bg-input p-4 text-center text-xl text-input-text"
/>
<div
className="font-roboto mt-2 flex h-4 w-36 cursor-pointer flex-row items-center justify-center rounded-xl border border-input-border-color bg-input p-5 text-center text-lg text-input-text"
onClick={handleChange}
>
Select
</div>
</div>
</div>
);
}
export default WispInput;

View file

@ -1,3 +1,3 @@
{
"prod": false
}
{
"prod": false
}

54
src/util/transports.ts Normal file
View file

@ -0,0 +1,54 @@
import {
SetTransport,
registerRemoteListener
} from "@mercuryworkshop/bare-mux";
declare global {
interface Window {
BareMux: any;
p: any;
}
}
function changeTransport(transport: string, wispUrl: string) {
switch (transport) {
case "epoxy":
localStorage.setItem("transport", "epoxy");
SetTransport("EpxMod.EpoxyClient", { wisp: wispUrl });
break;
//libcurl when supported can be easily added here
//and stuff like bare-as-module3 COULD also be added
default:
SetTransport("EpxMod.EpoxyClient", { wisp: wispUrl });
break;
}
}
function getTransport() {
return localStorage.getItem("transport") || "epoxy";
}
function restartTransport() {
changeTransport(
getTransport(),
localStorage.getItem("wispUrl") ||
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/"
);
}
//restart transport every minute
setInterval(restartTransport, 60000); //60000ms = 60s = 1m
const wispUrl =
(location.protocol === "https:" ? "wss://" : "ws://") +
location.host +
"/wisp/";
registerRemoteListener(navigator.serviceWorker.controller!);
changeTransport(
localStorage.getItem("transport") || "epoxy",
localStorage.getItem("wispUrl") || wispUrl
);
export { changeTransport, getTransport };

View file

@ -2,23 +2,34 @@ import million from "million/compiler";
import { defineConfig } from "vite";
import preact from "@preact/preset-vite";
import { viteStaticCopy } from "vite-plugin-static-copy";
import { uvPath } from "@nebula-services/ultraviolet";
import { uvPath } from "@titaniumnetwork-dev/ultraviolet";
import { dynamicPath } from "@nebula-services/dynamic";
import { epoxyPath } from "@mercuryworkshop/epoxy-transport";
import { baremuxPath } from "@mercuryworkshop/bare-mux";
import path from "path";
const __dirname = path.resolve();
console.log(dynamicPath);
export default defineConfig({
plugins: [
viteStaticCopy({
targets: [
{
// .replace fixes weird paths on Windows
src: `${uvPath}/uv.*.js`.replace(/\\/g, "/"),
src: `${uvPath}/**/*`.replace(/\\/g, "/"),
dest: "uv",
overwrite: false
},
//{
// src: `${baremuxPath}/**/*`.replace(/\\/g, "/"),
// dest: "mux",
// overwrite: false
//},
{
//include ALL files types
src: `${epoxyPath}/**/*`,
dest: "epoxy",
overwrite: false
},
{
// .replace fixes weird paths on Windows
src: `${dynamicPath}/dynamic.*.js`.replace(/\\/g, "/"),