diff --git a/.env b/.env new file mode 100644 index 0000000..a01f1f2 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +MASQR=1 \ No newline at end of file diff --git a/Checkfailed.html b/Checkfailed.html index 1bf52a5..05077f4 100644 --- a/Checkfailed.html +++ b/Checkfailed.html @@ -19,7 +19,7 @@ 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 - Refresh this page + Refresh this page

@@ -30,5 +30,11 @@

Thank you for using nginx.

+ diff --git a/index.html b/index.html index 2f93031..00b96d6 100644 --- a/index.html +++ b/index.html @@ -20,5 +20,14 @@
+ + diff --git a/package.json b/package.json index c08633b..0905a7d 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,9 @@ "@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", + "cookie-parser": "^1.4.6", "crypto-js": "^4.2.0", "fastify": "^4.25.1", "framer-motion": "^10.16.16", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3b98b1e..f09fe4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,9 +23,15 @@ dependencies: '@nebula-services/ultraviolet': specifier: 1.0.1-1.patch.7 version: 1.0.1-1.patch.7 + chalk: + specifier: ^5.3.0 + version: 5.3.0 classnames: specifier: ^2.3.2 version: 2.3.2 + cookie-parser: + specifier: ^1.4.6 + version: 1.4.6 crypto-js: specifier: ^4.2.0 version: 4.2.0 @@ -2225,11 +2231,28 @@ packages: /convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + /cookie-parser@1.4.6: + resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==} + engines: {node: '>= 0.8.0'} + dependencies: + cookie: 0.4.1 + cookie-signature: 1.0.6 + dev: false + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + /cookie-signature@1.2.1: resolution: {integrity: sha512-78KWk9T26NhzXtuL26cIJ8/qNHANyJ/ZYrmEXFzUmhZdjpBv+DlWlOANRTGBt48YcyslsLrj0bMLFTmXvLRCOw==} engines: {node: '>=6.6.0'} dev: false + /cookie@0.4.1: + resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} + engines: {node: '>= 0.6'} + dev: false + /cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} diff --git a/server.ts b/server.ts index 9d8672b..8e58a99 100644 --- a/server.ts +++ b/server.ts @@ -1,160 +1,167 @@ -import fastify from "fastify"; -import fastifyStatic from "@fastify/static"; +import { createBareServer } from '@nebula-services/bare-server-node'; +import chalk from "chalk"; +import express from 'express'; +import { createServer } from 'node:http'; import { fileURLToPath } from "url"; +import createRammerhead from 'rammerhead/src/server/index.js'; import path from "path"; import fs from "fs"; -import cookieParser from "@fastify/cookie"; -import { createServer } from "http"; - -import { createBareServer } from "@nebula-services/bare-server-node"; -import createRammerhead from "rammerhead/src/server/index.js"; +import cookieParser from "cookie-parser"; import wisp from "wisp-server-node"; import { Socket } from "net"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); - -const bare = createBareServer("/bare/"); -const rh = createRammerhead(); - -const failureFile = fs.readFileSync("Checkfailed.html", "utf8"); - const LICENSE_SERVER_URL = "https://license.mercurywork.shop/validate?license="; - +const whiteListedDomains = ["nebulaproxy.io"]; // Add any public domains you have here +const failureFile = fs.readFileSync("Checkfailed.html", "utf8"); +const rh = createRammerhead(); const rammerheadScopes = [ - "/rammerhead.js", - "/hammerhead.js", - "/transport-worker.js", - "/task.js", - "/iframe-task.js", - "/worker-hammerhead.js", - "/messaging", - "/sessionexists", - "/deletesession", - "/newsession", - "/editsession", - "/needpassword", - "/syncLocalStorage", - "/api/shuffleDict", - "/mainport" + '/rammerhead.js', + '/hammerhead.js', + '/transport-worker.js', + '/task.js', + '/iframe-task.js', + '/worker-hammerhead.js', + '/messaging', + '/sessionexists', + '/deletesession', + '/newsession', + '/editsession', + '/needpassword', + '/syncLocalStorage', + '/api/shuffleDict', ]; - const rammerheadSession = /^\/[a-z0-9]{32}/; +console.log(`${chalk.magentaBright('Starting Nebula...')}\n`); + +const app = express(); + +app.use(cookieParser()) + +// Congratulations! Masqr failed to validate, this is either your first visit or you're a FRAUD +async function MasqFail(req, res) { + if (!req.headers.host) { + // no bitch still using HTTP/1.0 go away + return; + } + const unsafeSuffix = req.headers.host + ".html" + let safeSuffix = path.normalize(unsafeSuffix).replace(/^(\.\.(\/|\\|$))+/, ''); + let safeJoin = path.join(process.cwd()+"/Masqrd", safeSuffix); + try { + await fs.promises.access(safeJoin) // man do I wish this was an if-then instead of a "exception on fail" + const failureFileLocal = await fs.promises.readFile(safeJoin, "utf8"); + res.setHeader("Content-Type", "text/html"); + res.send(failureFileLocal); + return; + } catch(e) { + res.setHeader("Content-Type", "text/html"); + res.send(failureFile); + return; + } +} + +// Woooooo masqr yayyyy (said no one) +// uncomment for masqr +/* app.use(async (req, res, next) => { + if (req.headers.host && whiteListedDomains.includes(req.headers.host)) { + next(); + return; + } + if (req.url.includes("/bare/")) { // replace this with your bare endpoint + next(); + return; + // Bypass for UV and other bares + } + + const authheader = req.headers.authorization; + + if (req.cookies["authcheck"]) { + next(); + return; + } + + + if (req.cookies['refreshcheck'] != "true") { + res.cookie("refreshcheck", "true", {maxAge: 10000}) // 10s refresh check + MasqFail(req, res) + return; + } + + if (!authheader) { + + res.setHeader('WWW-Authenticate', 'Basic'); // Yeah so we need to do this to get the auth params, kinda annoying and just showing a login prompt gives it away so its behind a 10s refresh check + res.status(401); + MasqFail(req, res) + return; + } + + const auth = Buffer.from(authheader.split(' ')[1], + 'base64').toString().split(':'); + const user = auth[0]; + const pass = auth[1]; + + const licenseCheck = ((await (await fetch(LICENSE_SERVER_URL + pass + "&host=" + req.headers.host)).json()))["status"] + console.log(LICENSE_SERVER_URL + pass + "&host=" + req.headers.host +" returned " +licenseCheck) + if (licenseCheck == "License valid") { + res.cookie("authcheck", "true", {expires: new Date((Date.now()) + (365*24*60*60 * 1000))}) // authorize session, for like a year, by then the link will be expired lol + res.send(``) // fun hack to make the browser refresh and remove the auth params from the URL + return; + } + + MasqFail(req, res) + return; +}) */ + +app.use(express.static("dist")); + +app.get('*', (req, res) => { + res.sendFile(path.join(__dirname, 'dist', 'index.html')); +}); + +const server = createServer(); + +const bare = createBareServer('/bare/'); + +server.on('request', (req, res) => { + if (bare.shouldRoute(req)) { + bare.routeRequest(req, res); + } else if (shouldRouteRh(req)) { + routeRhRequest(req, res); + } else { + app(req, res); + } +}); + +server.on('upgrade', (req, socket, 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); + } +}); + function shouldRouteRh(req) { - const url = new URL(req.url, "http://0.0.0.0"); - return ( - rammerheadScopes.includes(url.pathname) || - rammerheadSession.test(url.pathname) - ); + const url = new URL(req.url, 'http://0.0.0.0'); + return ( + rammerheadScopes.includes(url.pathname) || + rammerheadSession.test(url.pathname) + ); } function routeRhRequest(req, res) { - rh.emit("request", req, res); + rh.emit('request', req, res); } function routeRhUpgrade(req, socket, head) { - rh.emit("upgrade", req, socket, head); + rh.emit('upgrade', req, socket, head); } -const serverFactory = (handler, opts) => { - return createServer() - .on("request", (req, res) => { - if (bare.shouldRoute(req)) { - bare.routeRequest(req, res); - } else if (shouldRouteRh(req)) { - routeRhRequest(req, res); - } else { - handler(req, res); - } - }) - .on("upgrade", (req, socket, 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); - } - }); -}; +const port = parseInt(process.env.PORT || "8080"); -const app = fastify({ logger: true, serverFactory }); - -app.register(cookieParser); -await app.register(import("@fastify/compress")); - -// Uncomment if you wish to add masqr. -/* -app.addHook("preHandler", async (req, reply) => { - if (req.cookies["authcheck"]) { - return reply; - } - - const authheader = req.headers.authorization; - - if (req.cookies["refreshcheck"] != "true") { - reply - .setCookie("refreshcheck", "true", { maxAge: 10000 }) - .type("text/html") - .send(failureFile); - return reply; - } - - if (!authheader) { - reply - .code(401) - .header("WWW-Authenticate", "Basic") - .type("text/html") - .send(failureFile); - return reply; - } - - const auth = Buffer.from(authheader.split(" ")[1], "base64") - .toString() - .split(":"); - const user = auth[0]; - const pass = auth[1]; - - const licenseCheck = ( - await ( - await fetch(`${LICENSE_SERVER_URL}${pass}&host=${req.headers.host}`) - ).json() - )["status"]; - console.log( - `${LICENSE_SERVER_URL}${pass}&host=${req.headers.host} returned ${licenseCheck}` - ); - - if (licenseCheck === "License valid") { - reply.setCookie("authcheck", "true"); - return reply; - } - - reply.type("text/html").send(failureFile); - return reply; -}); */ - -app.register(fastifyStatic, { - root: path.join(__dirname, "dist"), - prefix: "/", - serve: true, - wildcard: false -}); - -app.get("/search=:query", async (req, res) => { - const { query } = req.params as { query: string }; // Define the type for req.params - - const response = await fetch( - `http://api.duckduckgo.com/ac?q=${query}&format=json` - ).then((apiRes) => apiRes.json()); - - res.send(response); -}); - -app.setNotFoundHandler((req, res) => { - res.sendFile("index.html"); // SPA catch-all -}); - -app.listen({ - port: 8080 -}); +server.listen(port, () => { + console.log(`${chalk.magentaBright("You can now use Nebula on port ") + chalk.bold(port)}\n`); +}); \ No newline at end of file