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