diff --git a/src/app.ts b/src/app.ts index bacdc926a..514d43362 100644 --- a/src/app.ts +++ b/src/app.ts @@ -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) diff --git a/src/controllers/custom/getItemListsController.ts b/src/controllers/custom/getItemListsController.ts new file mode 100644 index 000000000..92eedda09 --- /dev/null +++ b/src/controllers/custom/getItemListsController.ts @@ -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 }; diff --git a/src/routes/custom.ts b/src/routes/custom.ts index 1ab4ff2c8..8a4218d7b 100644 --- a/src/routes/custom.ts +++ b/src/routes/custom.ts @@ -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); diff --git a/src/routes/webui.ts b/src/routes/webui.ts new file mode 100644 index 000000000..5a16b5e48 --- /dev/null +++ b/src/routes/webui.ts @@ -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 }; diff --git a/src/services/itemDataService.ts b/src/services/itemDataService.ts index a251b9ae8..f46bc72d8 100644 --- a/src/services/itemDataService.ts +++ b/src/services/itemDataService.ts @@ -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; -type MinItem = Omit; +export type MinWarframe = Omit; +export type MinWeapon = Omit; +export type MinItem = Omit; -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; diff --git a/static/webui/index.html b/static/webui/index.html new file mode 100644 index 000000000..1df68c71b --- /dev/null +++ b/static/webui/index.html @@ -0,0 +1,65 @@ + + + + OpenWF WebUI + + + +
+

OpenWF WebUI

+
+

Login using your OpenWF account credentials.

+
+ + +
+ + +
+ +
+
+
+

Hello, ! Logout

+
+
+
Acquire Warframe
+
+
+ +
+
+ +
+
+
+
+
Acquire Weapon
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + diff --git a/static/webui/script.js b/static/webui/script.js new file mode 100644 index 000000000..6446c62c4 --- /dev/null +++ b/static/webui/script.js @@ -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"); +});