ui stuff, proxy?, and whatever else

This commit is contained in:
first@lynx.com
2026-08-17 14:19:11 -04:00
parent 9b6af70e71
commit a6ac6aaa4f
5 changed files with 151 additions and 22 deletions

View File

@ -17,6 +17,11 @@ import { getRawData, generateAccountPage, editProfile, saveData, getUsers, isAdm
import { callAI } from "./ai.js";
import { Readable } from 'stream';
import os from "node:os";
import crypto from "node:crypto";
import { server as wisp, logging as wispLogging } from "@mercuryworkshop/wisp-js/server";
import { scramjetPath } from "@mercuryworkshop/scramjet/path";
import { libcurlPath } from "@mercuryworkshop/libcurl-transport";
import { baremuxPath } from "@mercuryworkshop/bare-mux/node";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@ -29,6 +34,19 @@ const port = process.env.PORT || 3000;
const app = express();
wispLogging.set_level(wispLogging.WARN);
Object.assign(wisp.options, {
allow_udp_streams: false,
hostname_blacklist: [
/^localhost$/, /^127\./, /^0\.0\.0\.0$/,
/^10\./, /^192\.168\./, /^172\.(1[6-9]|2\d|3[01])\./,
/^169\.254\./, /^::1$/, /^\[::1\]$/,
],
});
app.use("/scram-assets/", express.static(scramjetPath));
app.use("/scram-assets/libcurl/", express.static(libcurlPath));
app.use("/scram-assets/baremux/", express.static(baremuxPath));
// why the fuck does this have to exist?
app.use("/resources/semag/hotline-miami/", (req, res, next) => {
if (req.method == "HEAD") {
@ -65,6 +83,42 @@ app.use(cookieParser());
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: false }));
app.use(express.text());
function proxyUnlockToken() {
if (!process.env.PROXY_PASSWORD) return null;
return crypto.createHash("sha256").update(process.env.PROXY_PASSWORD).digest("hex");
}
function isProxyUnlocked(req) {
const expected = proxyUnlockToken();
if (!expected) return true;
return req.cookies.proxy_unlock === expected;
}
function isProxyUnlockedRawCookieHeader(cookieHeader) {
const expected = proxyUnlockToken();
if (!expected) return true;
const match = (cookieHeader || "").split(";").map((c) => c.trim()).find((c) => c.startsWith("proxy_unlock="));
if (!match) return false;
return decodeURIComponent(match.slice("proxy_unlock=".length)) === expected;
}
app.post("/api/proxy/unlock", (req, res) => {
if (!process.env.PROXY_PASSWORD) {
res.status(400).send({ success: false, message: "beta access is not configured." });
return;
}
if (req.body.password === process.env.PROXY_PASSWORD) {
res.cookie("proxy_unlock", proxyUnlockToken(), {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 365,
sameSite: "lax",
});
res.status(200).send({ success: true });
} else {
res.status(401).send({ success: false, message: "incorrect password." });
}
});
app.get("/api/proxy/status", (req, res) => {
res.status(200).send({ unlocked: isProxyUnlocked(req) });
});
// app.use("/", (req, res, next) => {
// next();
// });
@ -174,7 +228,7 @@ app.post("/api/account/upload", async (req, res, next) => {
app.post("/api/analytics/game", async (req, res) => {
if (req.body && req.body.path.length < 32) {
let game = req.body.path;
if(game == "nso_fix") game = "nso";
if (game == "nso_fix") game = "nso";
const query = top.prepare(`
INSERT INTO stats (game, plays)
VALUES ($id, 1)
@ -193,7 +247,7 @@ let analyticsCache = null;
let analyticsCacheTime = 0;
const ANALYTICS_TTL = 10 * 1000;
app.use("/api/analytics/get", async(req, res) => {
app.use("/api/analytics/get", async (req, res) => {
const now = Date.now();
if (analyticsCache && (now - analyticsCacheTime) < ANALYTICS_TTL) {
res.send(analyticsCache);
@ -217,8 +271,8 @@ app.use("/api/analytics/get", async(req, res) => {
res.send(analyticsCache);
return;
});
app.use("/metrics", async(req, res) => {
const sql = `
app.use("/metrics", async (req, res) => {
const sql = `
SELECT
CASE
WHEN game = 'nso_fix' THEN 'nso'
@ -232,13 +286,13 @@ const sql = `
const query = top.prepare(sql);
let data = query.all();
let returnData = "#HELP game_plays_total The total number of plays per game.\n";
returnData+="#TYPE game_plays_total counter\n"
returnData += "#TYPE game_plays_total counter\n"
data.forEach(e => {
returnData+=`game_plays_total{name="${e.name}"} ${e.plays}\n`
returnData += `game_plays_total{name="${e.name}"} ${e.plays}\n`
});
returnData+="#HELP online_users The total number of people online.\n";
returnData+="#TYPE online_users gauge\n"
returnData+=`online_users ${onlineUsers}\n`
returnData += "#HELP online_users The total number of people online.\n";
returnData += "#TYPE online_users gauge\n"
returnData += `online_users ${onlineUsers}\n`
res.setHeader("Content-Type", "text/plain; version=0.0.4")
res.send(returnData);
return;
@ -361,6 +415,16 @@ app.use("/ai", async (req, res, next) => {
next();
}
});
app.use(["/proxy.html", "/proxy"], async (req, res, next) => {
if (isProxyUnlocked(req)) {
next();
return;
}
res
.type("text/html")
.status(200)
.send(await fs.readFile("./public/proxy-locked.html"));
});
app.use("/", express.static("./public", { extensions: ["html"] }));
app.use("/data/:id/:file", async (req, res) => {
const id = path.basename(req.params.id);
@ -466,6 +530,14 @@ const server = app.listen(port, () => {
console.log("- " + log.info("http://localhost:" + port));
});
server.on("upgrade", (request, socket, head) => {
if (request.url.startsWith("/wisp/")) {
if (!isProxyUnlockedRawCookieHeader(request.headers.cookie)) {
socket.destroy();
return;
}
wisp.routeRequest(request, socket, head);
return;
}
wss.handleUpgrade(request, socket, head, (socket) => {
wss.emit("connection", socket, request);
});