commit
a46908389c
20 changed files with 966 additions and 393 deletions
5
.dockerignore
Normal file
5
.dockerignore
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
node_modules/
|
||||
.vscode
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
.github/
|
||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
FROM node:21-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json .
|
||||
COPY . .
|
||||
|
||||
RUN npm i -g pnpm
|
||||
RUN pnpm install
|
||||
RUN pnpm run build
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["pnpm", "start"]
|
||||
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
nebula:
|
||||
image: nebula:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: nebula
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
# Host:Container (DO NOT MODIFY THE CONTAINER PORT)
|
||||
- "8081:8080"
|
||||
28
index.html
28
index.html
|
|
@ -4,12 +4,31 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<script src="/epoxy/index.js" defer></script>
|
||||
<script src="/libcurl/index.cjs" defer></script>
|
||||
<script src="/libcurl/index.js" defer></script>
|
||||
<script src="/transports/bareTransport.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>
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker
|
||||
.register("/sw.js", {
|
||||
scope: "/~/"
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Service Worker Registered");
|
||||
try {
|
||||
window.setTransport();
|
||||
} catch {}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Service Worker Failed to Register", err);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="margin: 0">
|
||||
<div id="app"></div>
|
||||
|
|
@ -22,10 +41,15 @@
|
|||
window.location.href = window.location.href;
|
||||
</script>
|
||||
<script>
|
||||
if (!localStorage["auth"] && new URL(document.all.rcheck.href).password) {
|
||||
try {
|
||||
if (
|
||||
!localStorage["auth"] &&
|
||||
new URL(document.all.rcheck.href).password
|
||||
) {
|
||||
window.location.reload();
|
||||
localStorage["auth"] = 1;
|
||||
}
|
||||
} catch {}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
20
package.json
20
package.json
|
|
@ -16,7 +16,7 @@
|
|||
"@fastify/static": "^6.12.0",
|
||||
"@mercuryworkshop/bare-mux": "^1.0.5",
|
||||
"@mercuryworkshop/epoxy-transport": "^1.1.0",
|
||||
"@mercuryworkshop/libcurl-transport": "^1.2.1",
|
||||
"@mercuryworkshop/libcurl-transport": "^1.2.4",
|
||||
"@nebula-services/dynamic": "0.7.2-patch.2",
|
||||
"@titaniumnetwork-dev/ultraviolet": "^3.0.0",
|
||||
"@tomphttp/bare-server-node": "2.0.3",
|
||||
|
|
@ -29,16 +29,16 @@
|
|||
"compression": "^1.7.4",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"crypto-js": "^4.2.0",
|
||||
"express": "^4.18.3",
|
||||
"express": "^4.19.1",
|
||||
"fastify": "^4.26.2",
|
||||
"framer-motion": "^10.18.0",
|
||||
"i18next": "^23.10.1",
|
||||
"i18next-browser-languagedetector": "^7.2.0",
|
||||
"localforage": "^1.10.0",
|
||||
"million": "^2.6.4",
|
||||
"preact": "^10.19.6",
|
||||
"preact": "^10.20.0",
|
||||
"preact-iso": "^2.4.0",
|
||||
"preact-render-to-string": "^6.4.0",
|
||||
"preact-render-to-string": "^6.4.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",
|
||||
|
|
@ -46,21 +46,21 @@
|
|||
"react-icons": "^4.12.0",
|
||||
"react-toastify": "^9.1.3",
|
||||
"tsx": "^4.7.1",
|
||||
"wisp-server-node": "^1.0.2",
|
||||
"wisp-server-node": "^1.0.4",
|
||||
"ws": "^8.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.8.1",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"@preact/preset-vite": "^2.8.2",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-preact": "^1.3.0",
|
||||
"postcss": "^8.4.35",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-tailwindcss": "^0.5.12",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.4.2",
|
||||
"vite": "^5.1.5",
|
||||
"typescript": "^5.4.3",
|
||||
"vite": "^5.2.2",
|
||||
"vite-plugin-static-copy": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1043
pnpm-lock.yaml
generated
1043
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,5 @@
|
|||
importScripts("/epoxy/index.js");
|
||||
importScripts("/libcurl/index.cjs");
|
||||
importScripts("/libcurl/index.js");
|
||||
importScripts("/transports/bareTransport.js");
|
||||
importScripts("/uv/uv.bundle.js");
|
||||
importScripts("/uv/uv.config.js");
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export function HeaderButton(props: HeaderButtonProps) {
|
|||
return (
|
||||
<Link href={href} className="flex h-full w-full bg-navbar-color sm:h-auto">
|
||||
<div className="group flex w-full flex-row items-center justify-center border-t-2 border-solid border-navbar-text-color p-4 md:border-none">
|
||||
<Icon className="h-10 w-10 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6" />
|
||||
<span className="font-roboto pl-1 text-center text-4xl font-bold text-text-color transition duration-500 group-hover:text-text-hover-color md:text-lg">
|
||||
<Icon className="h-6 w-6 text-text-color transition duration-500 group-hover:text-text-hover-color md:h-6 md:w-6" />
|
||||
<span className="font-roboto pl-1 text-center text-3xl font-bold text-text-color transition duration-500 group-hover:text-text-hover-color md:text-lg">
|
||||
{t(translationKey)}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import prod from "./config.json"; // Set prod to true if you wish to load balanc
|
|||
import { enc } from "../aes";
|
||||
import CloakedHead from "../util/CloakedHead";
|
||||
import { useEffect } from "preact/hooks";
|
||||
import { setTransport } from "../util/transports";
|
||||
import { updateServiceWorkers } from "../util/SWHelper.js";
|
||||
|
||||
export function Home() {
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
|
@ -18,6 +20,10 @@ export function Home() {
|
|||
const handleLoad = () => {
|
||||
const firstLoad = localStorage.getItem("firstLoad") || "true";
|
||||
console.log(firstLoad);
|
||||
//make sure service workers are updated
|
||||
updateServiceWorkers();
|
||||
//make sure transport is set
|
||||
//setTransport();
|
||||
if (firstLoad == "true" && prod) {
|
||||
function changeBare(url: string) {
|
||||
set("bare", url);
|
||||
|
|
@ -86,6 +92,8 @@ export function Home() {
|
|||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
//ensure transport is set
|
||||
setTransport();
|
||||
window.location.href =
|
||||
"/go/" +
|
||||
encodeURIComponent(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { HeaderRoute } from "../components/HeaderRoute";
|
||||
import { setTransport } from "../util/transports";
|
||||
interface Window {
|
||||
__uv$config: any;
|
||||
}
|
||||
export function Radon() {
|
||||
//make sure there is a transport set
|
||||
setTransport();
|
||||
return (
|
||||
<HeaderRoute>
|
||||
<iframe
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const CloakPreset = (props: Props) => {
|
|||
return (
|
||||
<div
|
||||
onClick={cloak}
|
||||
className="cursor-pointer rounded-full border border-input-border-color bg-lighter"
|
||||
className="flex h-16 w-16 cursor-pointer rounded-full border border-input-border-color bg-lighter"
|
||||
>
|
||||
<img
|
||||
src={props.faviconUrl === "none" ? "/logo.png" : props.faviconUrl}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ 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";
|
||||
import TransportDropdown from "./transportDropdown";
|
||||
|
||||
const Proxy = ({ id, active }) => {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -118,10 +118,10 @@ const Proxy = ({ id, active }) => {
|
|||
<div className="text-md p-4 font-bold text-input-text">
|
||||
Select the transport to use
|
||||
</div>
|
||||
<Dropdown
|
||||
<TransportDropdown
|
||||
storageKey="transport"
|
||||
options={transports}
|
||||
refresh={true}
|
||||
refresh={false}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const TabSettings = ({ id, active }) => {
|
|||
<div className="text-md pb-5 font-bold text-input-text">
|
||||
{t("settings.cloaking.subtitle")}
|
||||
</div>
|
||||
<div className="flex flex-row space-x-4">
|
||||
<div className="flex flex-row flex-wrap items-center justify-center gap-4">
|
||||
<CloakPreset faviconUrl="none" title="none" />
|
||||
<CloakPreset
|
||||
faviconUrl="https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://google.com&size=32"
|
||||
|
|
|
|||
80
src/pages/Settings/transportDropdown.tsx
Normal file
80
src/pages/Settings/transportDropdown.tsx
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { FaAngleDown } from "react-icons/fa";
|
||||
import { useState, useEffect } from "preact/hooks";
|
||||
import { changeTransport } from "../../util/transports.ts";
|
||||
const wispUrl =
|
||||
localStorage.getItem("wispUrl") ||
|
||||
(location.protocol === "https:" ? "wss://" : "ws://") +
|
||||
location.host +
|
||||
"/wisp/";
|
||||
|
||||
interface Option {
|
||||
id: string;
|
||||
label: string; // Translations CAN be passed
|
||||
}
|
||||
|
||||
const TransportDropdown = ({
|
||||
storageKey,
|
||||
options,
|
||||
refresh
|
||||
}: {
|
||||
storageKey: string;
|
||||
options: Option[];
|
||||
refresh: boolean;
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const [choice, setChoice] = useState(() => {
|
||||
return localStorage.getItem(storageKey) || options[0]?.id || "";
|
||||
});
|
||||
|
||||
// update on localstorage change
|
||||
useEffect(() => {
|
||||
setChoice(localStorage.getItem(storageKey) || options[0]?.id || "");
|
||||
}, [storageKey, options]);
|
||||
|
||||
return (
|
||||
<div className="relative text-center">
|
||||
<div
|
||||
className={`font-roboto flex h-14 w-56 cursor-pointer flex-col items-center justify-center border border-input-border-color bg-input text-center text-xl ${
|
||||
isOpen ? "rounded-t-2xl" : "rounded-2xl"
|
||||
}`}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<div className="flex h-full w-full select-none flex-row items-center">
|
||||
<div className="h-full w-1/4"></div>
|
||||
<div className="flex w-2/4 flex-col items-center text-input-text">
|
||||
{options.find((o) => o.id === choice)?.label}
|
||||
</div>
|
||||
<div className="flex w-1/4 flex-col items-center text-input-text">
|
||||
<FaAngleDown />
|
||||
</div>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div className="absolute top-full w-full">
|
||||
{options.map((option, index) => (
|
||||
<div
|
||||
key={option.id}
|
||||
className={`border border-input-border-color bg-input p-2 text-input-text hover:bg-dropdown-option-hover-color ${
|
||||
index === options.length - 1 ? "rounded-b-2xl" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
setChoice(option.id);
|
||||
localStorage.setItem(storageKey, option.id);
|
||||
changeTransport(option.id, wispUrl);
|
||||
if (refresh === true) {
|
||||
window.location.reload();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransportDropdown;
|
||||
|
|
@ -13,14 +13,16 @@ export function NotFound() {
|
|||
originalFavicon="/logo.png"
|
||||
/>
|
||||
<section className="h-full">
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<div className="flex h-full flex-col items-center justify-center text-center">
|
||||
<img src="/404.png" className="h-72"></img>
|
||||
<div className="flex flex-col items-center p-6">
|
||||
<p className="font-roboto text-4xl font-bold">{t("404.text")}</p>
|
||||
<span className="font-roboto text-3xl">404</span>
|
||||
<p className="font-roboto text-4xl font-bold text-text-color">
|
||||
{t("404.text")}
|
||||
</p>
|
||||
<span className="font-roboto text-3xl text-text-color">404</span>
|
||||
</div>
|
||||
<Link href="/">
|
||||
<button className="font-roboto h-14 w-44 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl placeholder:text-input-text focus:outline-none">
|
||||
<button className="font-roboto h-14 w-44 rounded-2xl border border-input-border-color bg-input p-2 text-center text-xl text-text-color placeholder:text-input-text focus:outline-none">
|
||||
{t("404.return")}
|
||||
</button>
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -7,23 +7,25 @@ import { Radon } from "./pages/Radon";
|
|||
import { Settings } from "./pages/Settings/";
|
||||
import { AboutBlank } from "./AboutBlank";
|
||||
import { Faq } from "./pages/Faq";
|
||||
//import { setTransport } from "./util/transports.js";
|
||||
|
||||
import "./style.css";
|
||||
import "./i18n";
|
||||
import { setTransport } from "./util/transports";
|
||||
|
||||
export default function Routes() {
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker
|
||||
.register("/sw.js", {
|
||||
scope: "/~/"
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Service worker registered successfully");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//if ("serviceWorker" in navigator) {
|
||||
//window.addEventListener("load", () => {
|
||||
// navigator.serviceWorker
|
||||
// .register("/sw.js", {
|
||||
// scope: "/~/"
|
||||
// })
|
||||
// .then(() => {
|
||||
// console.log("Service worker registered successfully");
|
||||
// setTransport();
|
||||
// });
|
||||
//});
|
||||
//}
|
||||
return (
|
||||
<LocationProvider>
|
||||
<Router>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { setTransport } from "./transports.ts";
|
||||
|
||||
function updateServiceWorkers() {
|
||||
navigator.serviceWorker.getRegistrations().then(function (registrations) {
|
||||
for (let registration of registrations) {
|
||||
|
|
@ -16,4 +18,17 @@ function uninstallServiceWorkers() {
|
|||
});
|
||||
}
|
||||
|
||||
export { updateServiceWorkers, uninstallServiceWorkers };
|
||||
function registerServiceWorker() {
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker
|
||||
.register("/sw.js", {
|
||||
scope: "/~/"
|
||||
})
|
||||
.then(() => {
|
||||
console.log("Service worker registered successfully");
|
||||
setTransport();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { updateServiceWorkers, uninstallServiceWorkers, registerServiceWorker };
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@ import {
|
|||
SetTransport,
|
||||
registerRemoteListener
|
||||
} from "@mercuryworkshop/bare-mux";
|
||||
//import { isIOS } from "./IosDetector";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
setTransport: () => void;
|
||||
}
|
||||
}
|
||||
|
||||
function changeTransport(transport: string, wispUrl: string) {
|
||||
switch (transport) {
|
||||
|
|
@ -15,7 +22,7 @@ function changeTransport(transport: string, wispUrl: string) {
|
|||
console.log("Setting transport to Libcurl");
|
||||
SetTransport("CurlMod.LibcurlClient", {
|
||||
wisp: wispUrl,
|
||||
wasm: "https://cdn.jsdelivr.net/npm/libcurl.js@v0.5.2/libcurl.wasm"
|
||||
wasm: "https://cdn.jsdelivr.net/npm/libcurl.js@v0.5.3/libcurl.wasm"
|
||||
});
|
||||
break;
|
||||
case "bare":
|
||||
|
|
@ -29,7 +36,7 @@ function changeTransport(transport: string, wispUrl: string) {
|
|||
default:
|
||||
SetTransport("CurlMod.LibcurlClient", {
|
||||
wisp: wispUrl,
|
||||
wasm: "/libcurl.wasm"
|
||||
wasm: "https://cdn.jsdelivr.net/npm/libcurl.js@v0.5.3/libcurl.wasm"
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -45,10 +52,32 @@ const wispUrl =
|
|||
"/wisp/";
|
||||
registerRemoteListener(navigator.serviceWorker.controller!);
|
||||
|
||||
//if (isIOS) {
|
||||
// console.log("iOS device detected. Bare will be used.");
|
||||
// changeTransport(
|
||||
// localStorage.getItem("transport") || "libcurl",
|
||||
// localStorage.getItem("wispUrl") || wispUrl
|
||||
// );
|
||||
//} else {
|
||||
// changeTransport(
|
||||
// localStorage.getItem("transport") || "bare",
|
||||
// localStorage.getItem("wispUrl") || wispUrl
|
||||
// );
|
||||
//}
|
||||
|
||||
changeTransport(
|
||||
//changeTransport(
|
||||
// localStorage.getItem("transport") || "libcurl",
|
||||
// localStorage.getItem("wispUrl") || wispUrl
|
||||
//);
|
||||
|
||||
// helper function for ../routes.tsx
|
||||
function setTransport() {
|
||||
changeTransport(
|
||||
localStorage.getItem("transport") || "libcurl",
|
||||
localStorage.getItem("wispUrl") || wispUrl
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export { changeTransport, getTransport };
|
||||
window.setTransport = setTransport;
|
||||
|
||||
export { changeTransport, getTransport, setTransport };
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import { dynamicPath } from "@nebula-services/dynamic";
|
|||
import { epoxyPath } from "@mercuryworkshop/epoxy-transport";
|
||||
import { libcurlPath } from "@mercuryworkshop/libcurl-transport";
|
||||
import path from "path";
|
||||
import { createBareServer } from "@tomphttp/bare-server-node";
|
||||
import wisp from "wisp-server-node";
|
||||
import http from "http";
|
||||
const __dirname = path.resolve();
|
||||
|
||||
export default defineConfig({
|
||||
|
|
@ -48,10 +51,16 @@ export default defineConfig({
|
|||
],
|
||||
server: {
|
||||
proxy: {
|
||||
"/bare": {
|
||||
"/bare/": {
|
||||
target: "http://localhost:8080/",
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/bare/, "")
|
||||
rewrite: (path) => path.replace(/^\/bare\//, "")
|
||||
},
|
||||
"/wisp/": {
|
||||
target: "http://ruby.rubynetwork.co/wisp/",
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(/^\/wisp\//, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue