This commit is contained in:
MotorTruck1221 2024-08-18 00:21:03 -06:00
parent 5ae0aad1d6
commit 3b00a749b5
No known key found for this signature in database
GPG key ID: 08F417E2B8B61EA4
13 changed files with 5887 additions and 2913 deletions

3
.gitignore vendored
View file

@ -25,3 +25,6 @@ pnpm-debug.log*
# nebula catalog database
database.sqlite
# Goofy PNPM problem
~/

View file

@ -2,13 +2,36 @@ import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";
import icon from "astro-icon";
import svelte from "@astrojs/svelte";
import node from "@astrojs/node";
import { viteStaticCopy } from "vite-plugin-static-copy";
import { baremuxPath } from '@mercuryworkshop/bare-mux';
import { epoxyPath } from '@mercuryworkshop/epoxy-transport';
import { uvPath } from '@titaniumnetwork-dev/ultraviolet';
// https://astro.build/config
export default defineConfig({
integrations: [tailwind(), icon(), svelte()],
vite: {
plugins: [
viteStaticCopy({
targets: [
{
src: `${uvPath}/**/*`.replace(/\\/g, '/'),
dest: 'uv',
overwrite: false
},
{
src: `${epoxyPath}/**/*`.replace(/\\/g, '/'),
dest: 'epoxy',
overwrite: false
},
{
src: `${baremuxPath}/**/*`.replace(/\\/g, '/'),
dest: 'baremux',
overwrite: false
}
]
})
],
server: {
proxy: {
"/api/catalog-assets": {
@ -24,6 +47,12 @@ export default defineConfig({
target: "http://localhost:8080",
changeOrigin: true,
},
"/wisp/" : {
target: "ws://localhost:8080/wisp/",
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(/^\/wisp\//, '')
},
"/styles": {
target: "http://localhost:8080",
changeOrigin: true,

View file

@ -18,6 +18,9 @@
"@fastify/compress": "^7.0.3",
"@fastify/static": "^7.0.4",
"@iconify-json/ph": "^1.1.13",
"@mercuryworkshop/bare-mux": "1.1.1",
"@mercuryworkshop/epoxy-transport": "2.0.1",
"@titaniumnetwork-dev/ultraviolet": "3.1.2",
"astro": "^4.12.2",
"astro-icon": "^1.1.0",
"concurrently": "^8.2.2",
@ -28,6 +31,8 @@
"sqlite3": "^5.1.7",
"svelte": "^4.2.18",
"tailwindcss": "^3.4.6",
"typescript": "^5.5.4"
"typescript": "^5.5.4",
"vite-plugin-static-copy": "^1.0.6",
"wisp-server-node": "^1.1.3"
}
}

8527
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

20
public/sw.js Normal file
View file

@ -0,0 +1,20 @@
importScripts('/epoxy/index.js');
importScripts('/uv/uv.bundle.js');
importScripts('/uv/uv.config.js');
importScripts(__uv$config.sw || '/uv/uv.sw.js');
const uv = new UVServiceWorker();
self.addEventListener('fetch', function (event) {
if (event.request.url.startsWith(location.origin + __uv$config.prefix)) {
event.respondWith(
(async function () {
return await uv.fetch(event);
})()
);
} else {
event.respondWith(
(async function () {
return await fetch(event.request);
})()
);
}
});

30
public/uv/uv.config.js Normal file
View file

@ -0,0 +1,30 @@
self.__uv$config = {
prefix: '/~/uv/',
bare: '/bare/',
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: '/uv/uv.handler.js',
client: '/uv/uv.client.js',
bundle: '/uv/uv.bundle.js',
config: '/uv/uv.config.js',
sw: '/uv/uv.sw.js'
};

View file

@ -1,6 +1,7 @@
import express from "express";
import { createServer } from "node:http";
import path from "path";
import wisp from "wisp-server-node";
import { Sequelize, DataTypes } from "sequelize";
import { fileURLToPath } from "url";
import { handler as ssrHandler } from "./dist/server/entry.mjs";
@ -176,6 +177,13 @@ server.on("request", (req, res) => {
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
app(req, res);
});
server.on("upgrade", (req, socket, head) => {
if (req.url.endsWith("/wisp/")) {
wisp.routeRequest(req, socket, head);
}
})
server.listen({
port: 8080,
});

View file

@ -0,0 +1,45 @@
<script>
//load all of the scripts required to use uv/rh (this is not loaded by default due to the size)
//Usage: await window.loadProxyScripts or window.loadProxyScript.then(() => {})
window.loadProxyScripts = function() {
//wrap everything in a promise to avoid race conditions
return new Promise<void>((resolve) => {
//create and append then scripts tags to the body (this is how we lazy load things)
const epoxyScript = document.createElement('script');
epoxyScript.src = '/epoxy/index.js';
epoxyScript.defer = true;
document.body.appendChild(epoxyScript);
const uvBundle = document.createElement('script');
uvBundle.src = '/uv/uv.bundle.js';
uvBundle.defer = true;
document.body.appendChild(uvBundle);
const uvConfig = document.createElement('script');
uvConfig.src = '/uv/uv.config.js';
uvConfig.defer = true;
document.body.appendChild(uvConfig);
const bareMux = document.createElement('script');
bareMux.src = '/baremux/bare.cjs';
bareMux.defer = true;
document.body.appendChild(bareMux);
const checkScripts = setInterval(() => {
//If both of these aren't defined this will repeat until they are
//this allows use to wait for all of the scripts to be ready *before* we setup the serviceworker
if (typeof EpxMod !== 'undefined' && typeof BareMux !== 'undefined') {
clearInterval(checkScripts);
resolve();
}
}, 100);
})
}
//function to set a transport between the *defined* transports
//Usage: await window.setTransport("epoxy") or window.setTransport("epoxy").then(() => {})
window.setTransport = function(transport?: string) {
//wrap in a promise so we don't register sw until a transport is set.
return new Promise<void>((resolve) => {
const wispUrl = (location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/wisp/";
BareMux.SetTransport("EpxMod.EpoxyClient", { wisp: wispUrl });
resolve();
});
}
</script>

4
src/env.d.ts vendored
View file

@ -1 +1,5 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
/// <reference types="@titaniumnetwork-dev/ultraviolet/client" />
declare var BareMux: any;
declare var EpxMod: any;

View file

@ -29,8 +29,36 @@ const t = useTranslations(lang);
</h1>
</div>
<input
id="nebula-input"
class="font-roboto h-14 rounded-t-2xl w-10/12 rounded-b-2xl border border-input-border-color bg-input p-2 text-center text-xl text-input-text placeholder:text-input-text roboto focus:outline-none md:w-3/12"
placeholder={t("home.placeholder")}
/>
<iframe id="neb-iframe" class="hidden z-100 w-full h-full absolute top-0 bottom-0 bg-primary"></iframe>
</div>
</Layout>
<script>
import { initSw } from "@utils/registerSW.ts"; //../../utils/registerSW.ts
import { search } from "@utils/search.ts"; //../../utils/search.ts
function proxy(term: string) {
return __uv$config!.prefix + __uv$config.encodeUrl!(search(term, "https://www.google.com/search?q=%s"));
}
//we need to rerun this on every page load
document.addEventListener("astro:page-load", function () {
//wrap this in a try catch as sometimes this element will not be available on the page
try {
const input = document.getElementById("nebula-input") as HTMLInputElement;
const iframe = document.getElementById("neb-iframe") as HTMLIframeElement;
input?.addEventListener("keypress", function (event: any) {
if (event.key === "Enter") {
initSw().then(() => {
iframe.classList.remove("hidden");
iframe.src = proxy(input?.value);
})
}
})
}
catch (_) {
//we purposely don't return anything
}
});
</script>

56
src/utils/registerSW.ts Normal file
View file

@ -0,0 +1,56 @@
function loadProxyScripts() {
//wrap everything in a promise to avoid race conditions
return new Promise<void>((resolve) => {
//create and append then scripts tags to the body (this is how we lazy load things)
const epoxyScript = document.createElement('script');
epoxyScript.src = '/epoxy/index.js';
epoxyScript.defer = true;
document.body.appendChild(epoxyScript);
const uvBundle = document.createElement('script');
uvBundle.src = '/uv/uv.bundle.js';
uvBundle.defer = true;
document.body.appendChild(uvBundle);
const uvConfig = document.createElement('script');
uvConfig.src = '/uv/uv.config.js';
uvConfig.defer = true;
document.body.appendChild(uvConfig);
const bareMux = document.createElement('script');
bareMux.src = '/baremux/bare.cjs';
bareMux.defer = true;
document.body.appendChild(bareMux);
const checkScripts = setInterval(() => {
//If both of these aren't defined this will repeat until they are
//this allows use to wait for all of the scripts to be ready *before* we setup the serviceworker
if (typeof EpxMod !== 'undefined' && typeof BareMux !== 'undefined') {
clearInterval(checkScripts);
resolve();
}
}, 100);
})
}
function setTransport(transport?: string) {
//wrap in a promise so we don't register sw until a transport is set.
return new Promise<void>((resolve) => {
const wispUrl = (location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/wisp/";
BareMux.SetTransport("EpxMod.EpoxyClient", { wisp: wispUrl });
resolve();
});
}
function initSw() {
//this is wrapped in a promise to mostly solve the bare-mux v1 problems
return new Promise<void>((resolve) => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(async () => {
console.debug('Service worker ready!');
await loadProxyScripts();
await setTransport();
resolve();
});
navigator.serviceWorker.register('/sw.js', { scope: '/' });
}
})
}
export { initSw, setTransport }

27
src/utils/search.ts Normal file
View file

@ -0,0 +1,27 @@
function search(input: string, template: string) {
try {
// input is a valid URL:
// eg: https://example.com, https://example.com/test?q=param
return new URL(input).toString();
} catch (err) {
// input was not a valid URL
}
try {
// input is a valid URL when http:// is added to the start:
// eg: example.com, https://example.com/test?q=param
const url = new URL(`http://${input}`);
// only if the hostname has a TLD/subdomain
if (url.hostname.includes('.')) return url.toString();
} catch (err) {
// input was not valid URL
}
// input may have been a valid URL, however the hostname was invalid
// Attempts to convert the input to a fully qualified URL have failed
// Treat the input as a search query
return template.replace('%s', encodeURIComponent(input));
}
export { search }

View file

@ -1,4 +1,10 @@
{
"extends": "astro/tsconfigs/strict",
"include": ["src"]
"include": ["src"],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@utils/*" : ["src/utils/*"]
}
}
}