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 { customRouter } from "@/src/routes/custom";
 | 
				
			||||||
import { dynamicController } from "@/src/routes/dynamic";
 | 
					import { dynamicController } from "@/src/routes/dynamic";
 | 
				
			||||||
import { statsRouter } from "@/src/routes/stats";
 | 
					import { statsRouter } from "@/src/routes/stats";
 | 
				
			||||||
 | 
					import { webuiRouter } from "@/src/routes/webui";
 | 
				
			||||||
import { connectDatabase } from "@/src/services/mongoService";
 | 
					import { connectDatabase } from "@/src/services/mongoService";
 | 
				
			||||||
import { registerLogFileCreationListener } from "@/src/utils/logger";
 | 
					import { registerLogFileCreationListener } from "@/src/utils/logger";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -35,6 +36,8 @@ app.use("/:id/dynamic", dynamicController);
 | 
				
			|||||||
app.post("/pay/steamPacks.php", steamPacksController);
 | 
					app.post("/pay/steamPacks.php", steamPacksController);
 | 
				
			||||||
app.use("/stats", statsRouter);
 | 
					app.use("/stats", statsRouter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.use("/", webuiRouter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.use(unknownEndpointHandler);
 | 
					app.use(unknownEndpointHandler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//app.use(errorHandler)
 | 
					//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 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();
 | 
					const customRouter = express.Router();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					customRouter.get("/getItemLists", getItemListsController);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
customRouter.post("/createAccount", createAccountController);
 | 
					customRouter.post("/createAccount", createAccountController);
 | 
				
			||||||
customRouter.post("/addItem", addItemController);
 | 
					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 { getIndexAfter } from "@/src/helpers/stringHelpers";
 | 
				
			||||||
import { logger } from "@/src/utils/logger";
 | 
					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">;
 | 
					export type MinWarframe = Omit<Warframe, "patchlogs">;
 | 
				
			||||||
type MinItem = Omit<Item, "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(
 | 
					export const warframes: MinWarframe[] = Array.from(new Items({ category: ["Warframes"] }) as Warframe[])
 | 
				
			||||||
    item => {
 | 
					    .filter(item => {
 | 
				
			||||||
 | 
					        return item.uniqueName.substring(0, 30) != "/Lotus/Powersuits/EntratiMech/";
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .map(item => {
 | 
				
			||||||
        const next = { ...item };
 | 
					        const next = { ...item };
 | 
				
			||||||
        delete next.patchlogs;
 | 
					        delete next.patchlogs;
 | 
				
			||||||
        return next;
 | 
					        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 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 };
 | 
					    const next = { ...item };
 | 
				
			||||||
    delete next.patchlogs;
 | 
					    delete next.patchlogs;
 | 
				
			||||||
    return next;
 | 
					    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