forked from OpenWF/SpaceNinjaServer
		
	feat: WebUI (#155)
This commit is contained in:
		
							parent
							
								
									675e6c4583
								
							
						
					
					
						commit
						c31d82b61e
					
				@ -12,6 +12,7 @@ import { steamPacksController } from "@/src/controllers/misc/steamPacksControlle
 | 
			
		||||
import { customRouter } from "@/src/routes/custom";
 | 
			
		||||
import { dynamicController } from "@/src/routes/dynamic";
 | 
			
		||||
import { statsRouter } from "@/src/routes/stats";
 | 
			
		||||
import { webuiRouter } from "@/src/routes/webui";
 | 
			
		||||
import { connectDatabase } from "@/src/services/mongoService";
 | 
			
		||||
import { registerLogFileCreationListener } from "@/src/utils/logger";
 | 
			
		||||
 | 
			
		||||
@ -35,6 +36,8 @@ app.use("/:id/dynamic", dynamicController);
 | 
			
		||||
app.post("/pay/steamPacks.php", steamPacksController);
 | 
			
		||||
app.use("/stats", statsRouter);
 | 
			
		||||
 | 
			
		||||
app.use("/", webuiRouter);
 | 
			
		||||
 | 
			
		||||
app.use(unknownEndpointHandler);
 | 
			
		||||
 | 
			
		||||
//app.use(errorHandler)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								src/controllers/custom/getItemListsController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/controllers/custom/getItemListsController.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
import { RequestHandler } from "express";
 | 
			
		||||
import { MinItem, warframes, weapons } from "@/src/services/itemDataService";
 | 
			
		||||
 | 
			
		||||
interface ListedItem {
 | 
			
		||||
    uniqueName: string;
 | 
			
		||||
    name: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function reduceItems(items: MinItem[]): ListedItem[] {
 | 
			
		||||
    return items.map((item: MinItem): ListedItem => {
 | 
			
		||||
        return {
 | 
			
		||||
            uniqueName: item.uniqueName,
 | 
			
		||||
            name: item.name
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const getItemListsController: RequestHandler = (_req, res) => {
 | 
			
		||||
    res.json({
 | 
			
		||||
        warframes: reduceItems(warframes),
 | 
			
		||||
        weapons: reduceItems(weapons)
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export { getItemListsController };
 | 
			
		||||
@ -1,9 +1,12 @@
 | 
			
		||||
import { addItemController } from "@/src/controllers/custom/addItemController";
 | 
			
		||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
 | 
			
		||||
import express from "express";
 | 
			
		||||
import { getItemListsController } from "@/src/controllers/custom/getItemListsController";
 | 
			
		||||
import { createAccountController } from "@/src/controllers/custom/createAccountController";
 | 
			
		||||
import { addItemController } from "@/src/controllers/custom/addItemController";
 | 
			
		||||
 | 
			
		||||
const customRouter = express.Router();
 | 
			
		||||
 | 
			
		||||
customRouter.get("/getItemLists", getItemListsController);
 | 
			
		||||
 | 
			
		||||
customRouter.post("/createAccount", createAccountController);
 | 
			
		||||
customRouter.post("/addItem", addItemController);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								src/routes/webui.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/routes/webui.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
import express from "express";
 | 
			
		||||
import path from "path";
 | 
			
		||||
 | 
			
		||||
const webuiRouter = express.Router();
 | 
			
		||||
 | 
			
		||||
webuiRouter.get("/", (_req, res) => {
 | 
			
		||||
    res.redirect("/webui/");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const rootDir = path.join(__dirname, "../..");
 | 
			
		||||
 | 
			
		||||
webuiRouter.get("/webui/", (req, res) => {
 | 
			
		||||
    if (req.path != "/webui/") {
 | 
			
		||||
        res.redirect("/webui/");
 | 
			
		||||
    } else {
 | 
			
		||||
        res.sendFile(path.join(rootDir, "static/webui/index.html"));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
webuiRouter.get("/webui/script.js", (_req, res) => {
 | 
			
		||||
    res.sendFile(path.join(rootDir, "static/webui/script.js"));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export { webuiRouter };
 | 
			
		||||
@ -1,21 +1,32 @@
 | 
			
		||||
import { getIndexAfter } from "@/src/helpers/stringHelpers";
 | 
			
		||||
import { logger } from "@/src/utils/logger";
 | 
			
		||||
import Items, { Buildable, Category, Item, Warframe, Weapon } from "warframe-items";
 | 
			
		||||
import Items, { Buildable, Category, MinimalItem, Warframe, Weapon } from "warframe-items";
 | 
			
		||||
 | 
			
		||||
type MinWeapon = Omit<Weapon, "patchlogs">;
 | 
			
		||||
type MinItem = Omit<Item, "patchlogs">;
 | 
			
		||||
export type MinWarframe = Omit<Warframe, "patchlogs">;
 | 
			
		||||
export type MinWeapon = Omit<Weapon, "patchlogs">;
 | 
			
		||||
export type MinItem = Omit<MinimalItem, "patchlogs">;
 | 
			
		||||
 | 
			
		||||
export const weapons: MinWeapon[] = (new Items({ category: ["Primary", "Secondary", "Melee"] }) as Weapon[]).map(
 | 
			
		||||
    item => {
 | 
			
		||||
export const warframes: MinWarframe[] = Array.from(new Items({ category: ["Warframes"] }) as Warframe[])
 | 
			
		||||
    .filter(item => {
 | 
			
		||||
        return item.uniqueName.substring(0, 30) != "/Lotus/Powersuits/EntratiMech/";
 | 
			
		||||
    })
 | 
			
		||||
    .map(item => {
 | 
			
		||||
        const next = { ...item };
 | 
			
		||||
        delete next.patchlogs;
 | 
			
		||||
        return next;
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
export const weapons: MinWeapon[] = Array.from(
 | 
			
		||||
    new Items({ category: ["Primary", "Secondary", "Melee"] }) as Weapon[]
 | 
			
		||||
).map(item => {
 | 
			
		||||
    const next = { ...item };
 | 
			
		||||
    delete next.patchlogs;
 | 
			
		||||
    return next;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export type WeaponTypeInternal = "LongGuns" | "Pistols" | "Melee";
 | 
			
		||||
 | 
			
		||||
export const items: MinItem[] = new Items({ category: ["All"] }).map(item => {
 | 
			
		||||
export const items: MinItem[] = Array.from(new Items({ category: ["All"] }) as MinimalItem[]).map(item => {
 | 
			
		||||
    const next = { ...item };
 | 
			
		||||
    delete next.patchlogs;
 | 
			
		||||
    return next;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										65
									
								
								static/webui/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								static/webui/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
<!doctype html>
 | 
			
		||||
<html lang="en" data-bs-theme="dark">
 | 
			
		||||
    <head>
 | 
			
		||||
        <title>OpenWF WebUI</title>
 | 
			
		||||
        <link
 | 
			
		||||
            href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
 | 
			
		||||
            rel="stylesheet"
 | 
			
		||||
            integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
 | 
			
		||||
            crossorigin="anonymous"
 | 
			
		||||
        />
 | 
			
		||||
    </head>
 | 
			
		||||
    <body>
 | 
			
		||||
        <div class="container pt-3 pb-3">
 | 
			
		||||
            <h1>OpenWF WebUI</h1>
 | 
			
		||||
            <div id="login-view">
 | 
			
		||||
                <p>Login using your OpenWF account credentials.</p>
 | 
			
		||||
                <form onsubmit="doLogin();return false;">
 | 
			
		||||
                    <label for="email">Email address</label>
 | 
			
		||||
                    <input class="form-control" type="email" id="email" required />
 | 
			
		||||
                    <br />
 | 
			
		||||
                    <label for="password">Password</label>
 | 
			
		||||
                    <input class="form-control" type="password" id="password" required />
 | 
			
		||||
                    <br />
 | 
			
		||||
                    <button class="btn btn-primary" type="submit">Login</button>
 | 
			
		||||
                </form>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div id="main-view" class="d-none">
 | 
			
		||||
                <p>Hello, <b class="displayname"></b>! <a href="#" onclick="logout();">Logout</a></p>
 | 
			
		||||
                <div class="d-flex">
 | 
			
		||||
                    <div class="card m-1 w-50">
 | 
			
		||||
                        <h5 class="card-header">Acquire Warframe</h5>
 | 
			
		||||
                        <form class="card-body row" onsubmit="doAcquireWarframe();return false;">
 | 
			
		||||
                            <div class="col-xxl-10">
 | 
			
		||||
                                <input class="form-control" id="warframe-to-acquire" list="datalist-warframes" />
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="col-xxl-2">
 | 
			
		||||
                                <button class="btn btn-primary" type="submit">Acquire</button>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="card m-1 w-50">
 | 
			
		||||
                        <h5 class="card-header">Acquire Weapon</h5>
 | 
			
		||||
                        <form class="card-body row" onsubmit="doAcquireWeapon();return false;">
 | 
			
		||||
                            <div class="col-xxl-10">
 | 
			
		||||
                                <input class="form-control" id="weapon-to-acquire" list="datalist-weapons" />
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="col-xxl-2">
 | 
			
		||||
                                <button class="btn btn-primary" type="submit">Acquire</button>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <datalist id="datalist-warframes"></datalist>
 | 
			
		||||
        <datalist id="datalist-weapons"></datalist>
 | 
			
		||||
        <script
 | 
			
		||||
            src="https://code.jquery.com/jquery-3.6.0.min.js"
 | 
			
		||||
            integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
 | 
			
		||||
            crossorigin="anonymous"
 | 
			
		||||
        ></script>
 | 
			
		||||
        <script src="https://cdn.jsdelivr.net/gh/angeal185/whirlpool-js/dist/whirlpool-js.min.js"></script>
 | 
			
		||||
        <script src="script.js"></script>
 | 
			
		||||
    </body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										110
									
								
								static/webui/script.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								static/webui/script.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
			
		||||
function doLogin() {
 | 
			
		||||
    localStorage.setItem("email", $("#email").val());
 | 
			
		||||
    localStorage.setItem("password", $("#password").val());
 | 
			
		||||
    loginFromLocalStorage();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function loginFromLocalStorage() {
 | 
			
		||||
    const req = $.post({
 | 
			
		||||
        url: "/api/login.php",
 | 
			
		||||
        contentType: "text/plain",
 | 
			
		||||
        data: JSON.stringify({
 | 
			
		||||
            email: localStorage.getItem("email"),
 | 
			
		||||
            password: wp.encSync(localStorage.getItem("password"), "hex"),
 | 
			
		||||
            time: parseInt(new Date() / 1000),
 | 
			
		||||
            s: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==", // signature of some kind
 | 
			
		||||
            lang: "en",
 | 
			
		||||
            date: 1501230947855458660, // ???
 | 
			
		||||
            ClientType: "",
 | 
			
		||||
            PS: "W0RFXVN0ZXZlIGxpa2VzIGJpZyBidXR0cw==" // anti-cheat data
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
    req.done(data => {
 | 
			
		||||
        $("#login-view").addClass("d-none");
 | 
			
		||||
        $("#main-view").removeClass("d-none");
 | 
			
		||||
        $(".displayname").text(data.DisplayName);
 | 
			
		||||
        window.accountId = data.id;
 | 
			
		||||
    });
 | 
			
		||||
    req.fail(() => {
 | 
			
		||||
        logout();
 | 
			
		||||
        alert("Login failed");
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function logout() {
 | 
			
		||||
    localStorage.removeItem("email");
 | 
			
		||||
    localStorage.removeItem("password");
 | 
			
		||||
    $("#login-view").removeClass("d-none");
 | 
			
		||||
    $("#main-view").addClass("d-none");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (localStorage.getItem("email") && localStorage.getItem("password")) {
 | 
			
		||||
    loginFromLocalStorage();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const req = $.get("/custom/getItemLists");
 | 
			
		||||
req.done(data => {
 | 
			
		||||
    for (const [type, items] of Object.entries(data)) {
 | 
			
		||||
        items.forEach(item => {
 | 
			
		||||
            const option = document.createElement("option");
 | 
			
		||||
            option.setAttribute("data-key", item.uniqueName);
 | 
			
		||||
            option.value = item.name;
 | 
			
		||||
            document.getElementById("datalist-" + type).appendChild(option);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function getKey(input) {
 | 
			
		||||
    return document
 | 
			
		||||
        .getElementById(input.getAttribute("list"))
 | 
			
		||||
        .querySelector("[value='" + input.value.split("'").join("\\'") + "']")
 | 
			
		||||
        ?.getAttribute("data-key");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function doAcquireWarframe() {
 | 
			
		||||
    const uniqueName = getKey(document.getElementById("warframe-to-acquire"));
 | 
			
		||||
    if (!uniqueName) {
 | 
			
		||||
        $("#warframe-to-acquire").addClass("is-invalid");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const req = $.post({
 | 
			
		||||
        url: "/custom/addItem",
 | 
			
		||||
        contentType: "application/json",
 | 
			
		||||
        data: JSON.stringify({
 | 
			
		||||
            type: "Powersuit",
 | 
			
		||||
            internalName: uniqueName,
 | 
			
		||||
            accountId: window.accountId
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
    req.done(() => {
 | 
			
		||||
        alert("Warframe added to your inventory! Visit navigation to force an inventory update.");
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$("#warframe-to-acquire").on("input", () => {
 | 
			
		||||
    $("#warframe-to-acquire").removeClass("is-invalid");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function doAcquireWeapon() {
 | 
			
		||||
    const uniqueName = getKey(document.getElementById("weapon-to-acquire"));
 | 
			
		||||
    if (!uniqueName) {
 | 
			
		||||
        $("#weapon-to-acquire").addClass("is-invalid");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const req = $.post({
 | 
			
		||||
        url: "/custom/addItem",
 | 
			
		||||
        contentType: "application/json",
 | 
			
		||||
        data: JSON.stringify({
 | 
			
		||||
            type: "Weapon",
 | 
			
		||||
            internalName: uniqueName,
 | 
			
		||||
            accountId: window.accountId
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
    req.done(() => {
 | 
			
		||||
        alert("Weapon added to your inventory! Visit navigation to force an inventory update.");
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$("#weapon-to-acquire").on("input", () => {
 | 
			
		||||
    $("#weapon-to-acquire").removeClass("is-invalid");
 | 
			
		||||
});
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user