feat: add administrators, require administrator perms to change server config in webui (#628)
This commit is contained in:
parent
eeaac6f07e
commit
103e9bc431
@ -8,6 +8,7 @@
|
|||||||
"myAddress": "localhost",
|
"myAddress": "localhost",
|
||||||
"httpPort": 80,
|
"httpPort": 80,
|
||||||
"httpsPort": 443,
|
"httpsPort": 443,
|
||||||
|
"administratorNames": [],
|
||||||
"autoCreateAccount": true,
|
"autoCreateAccount": true,
|
||||||
"skipStoryModeChoice": true,
|
"skipStoryModeChoice": true,
|
||||||
"skipTutorial": true,
|
"skipTutorial": true,
|
||||||
|
@ -6,7 +6,7 @@ import buildConfig from "@/static/data/buildConfig.json";
|
|||||||
|
|
||||||
import { toLoginRequest } from "@/src/helpers/loginHelpers";
|
import { toLoginRequest } from "@/src/helpers/loginHelpers";
|
||||||
import { Account } from "@/src/models/loginModel";
|
import { Account } from "@/src/models/loginModel";
|
||||||
import { createAccount, isCorrectPassword } from "@/src/services/loginService";
|
import { createAccount, isCorrectPassword, isNameTaken } from "@/src/services/loginService";
|
||||||
import { IDatabaseAccountJson, ILoginResponse } from "@/src/types/loginTypes";
|
import { IDatabaseAccountJson, ILoginResponse } from "@/src/types/loginTypes";
|
||||||
import { DTLS, groups, HUB, platformCDNs } from "@/static/fixed_responses/login_static";
|
import { DTLS, groups, HUB, platformCDNs } from "@/static/fixed_responses/login_static";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
@ -26,10 +26,19 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
|
|
||||||
if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
|
if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
|
||||||
try {
|
try {
|
||||||
|
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
|
||||||
|
let name = nameFromEmail;
|
||||||
|
if (await isNameTaken(name)) {
|
||||||
|
let suffix = 0;
|
||||||
|
do {
|
||||||
|
++suffix;
|
||||||
|
name = nameFromEmail + suffix;
|
||||||
|
} while (await isNameTaken(name));
|
||||||
|
}
|
||||||
const newAccount = await createAccount({
|
const newAccount = await createAccount({
|
||||||
email: loginRequest.email,
|
email: loginRequest.email,
|
||||||
password: loginRequest.password,
|
password: loginRequest.password,
|
||||||
DisplayName: loginRequest.email.substring(0, loginRequest.email.indexOf("@")),
|
DisplayName: name,
|
||||||
CountryCode: loginRequest.lang.toUpperCase(),
|
CountryCode: loginRequest.lang.toUpperCase(),
|
||||||
ClientType: loginRequest.ClientType,
|
ClientType: loginRequest.ClientType,
|
||||||
CrossPlatformAllowed: true,
|
CrossPlatformAllowed: true,
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { toCreateAccount, toDatabaseAccount } from "@/src/helpers/customHelpers/customHelpers";
|
import { toCreateAccount, toDatabaseAccount } from "@/src/helpers/customHelpers/customHelpers";
|
||||||
import { createAccount } from "@/src/services/loginService";
|
import { createAccount, isNameTaken } from "@/src/services/loginService";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
const createAccountController: RequestHandler = async (req, res) => {
|
const createAccountController: RequestHandler = async (req, res) => {
|
||||||
const createAccountData = toCreateAccount(req.body);
|
const createAccountData = toCreateAccount(req.body);
|
||||||
const databaseAccount = toDatabaseAccount(createAccountData);
|
if (await isNameTaken(createAccountData.DisplayName)) {
|
||||||
|
res.status(409).json("Name already in use");
|
||||||
const account = await createAccount(databaseAccount);
|
} else {
|
||||||
|
const databaseAccount = toDatabaseAccount(createAccountData);
|
||||||
res.json(account);
|
const account = await createAccount(databaseAccount);
|
||||||
|
res.json(account);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { createAccountController };
|
export { createAccountController };
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { config } from "@/src/services/configService";
|
import { config } from "@/src/services/configService";
|
||||||
|
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
|
||||||
|
|
||||||
const getConfigDataController: RequestHandler = (_req, res) => {
|
const getConfigDataController: RequestHandler = async (req, res) => {
|
||||||
res.json(config);
|
const account = await getAccountForRequest(req);
|
||||||
|
if (isAdministrator(account)) {
|
||||||
|
res.json(config);
|
||||||
|
} else {
|
||||||
|
res.status(401).end();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getConfigDataController };
|
export { getConfigDataController };
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { getAccountForRequest } from "@/src/services/loginService";
|
import { getAccountForRequest, isNameTaken } from "@/src/services/loginService";
|
||||||
|
|
||||||
export const renameAccountController: RequestHandler = async (req, res) => {
|
export const renameAccountController: RequestHandler = async (req, res) => {
|
||||||
const account = await getAccountForRequest(req);
|
const account = await getAccountForRequest(req);
|
||||||
if (typeof req.query.newname == "string") {
|
if (typeof req.query.newname == "string") {
|
||||||
account.DisplayName = req.query.newname;
|
if (await isNameTaken(req.query.newname)) {
|
||||||
await account.save();
|
res.status(409).json("Name already in use");
|
||||||
res.end();
|
} else {
|
||||||
|
account.DisplayName = req.query.newname;
|
||||||
|
await account.save();
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
res.status(400).end();
|
res.status(400).end();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { updateConfig } from "@/src/services/configService";
|
import { updateConfig } from "@/src/services/configService";
|
||||||
|
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
|
||||||
|
|
||||||
const updateConfigDataController: RequestHandler = async (req, res) => {
|
const updateConfigDataController: RequestHandler = async (req, res) => {
|
||||||
await updateConfig(String(req.body));
|
const account = await getAccountForRequest(req);
|
||||||
res.end();
|
if (isAdministrator(account)) {
|
||||||
|
await updateConfig(String(req.body));
|
||||||
|
res.end();
|
||||||
|
} else {
|
||||||
|
res.status(401).end();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { updateConfigDataController };
|
export { updateConfigDataController };
|
||||||
|
@ -24,7 +24,7 @@ const databaseAccountSchema = new Schema<IDatabaseAccountJson>(
|
|||||||
{
|
{
|
||||||
email: { type: String, required: true, unique: true },
|
email: { type: String, required: true, unique: true },
|
||||||
password: { type: String, required: true },
|
password: { type: String, required: true },
|
||||||
DisplayName: { type: String, required: true },
|
DisplayName: { type: String, required: true, unique: true },
|
||||||
CountryCode: { type: String, required: true },
|
CountryCode: { type: String, required: true },
|
||||||
ClientType: { type: String },
|
ClientType: { type: String },
|
||||||
CrossPlatformAllowed: { type: Boolean, required: true },
|
CrossPlatformAllowed: { type: Boolean, required: true },
|
||||||
|
@ -14,6 +14,13 @@ fs.watchFile(configPath, () => {
|
|||||||
amnesia = false;
|
amnesia = false;
|
||||||
} else {
|
} else {
|
||||||
logger.info("Detected a change to config.json, reloading its contents.");
|
logger.info("Detected a change to config.json, reloading its contents.");
|
||||||
|
|
||||||
|
// Set all values to undefined now so if the new config.json omits some fields that were previously present, it's correct in-memory.
|
||||||
|
for (const key of Object.keys(config)) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
||||||
|
(config as any)[key] = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
Object.assign(config, JSON.parse(fs.readFileSync(configPath, "utf-8")));
|
Object.assign(config, JSON.parse(fs.readFileSync(configPath, "utf-8")));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -25,6 +32,7 @@ interface IConfig {
|
|||||||
httpPort?: number;
|
httpPort?: number;
|
||||||
httpsPort?: number;
|
httpsPort?: number;
|
||||||
myIrcAddresses?: string[];
|
myIrcAddresses?: string[];
|
||||||
|
administratorNames?: string[];
|
||||||
autoCreateAccount?: boolean;
|
autoCreateAccount?: boolean;
|
||||||
skipStoryModeChoice?: boolean;
|
skipStoryModeChoice?: boolean;
|
||||||
skipTutorial?: boolean;
|
skipTutorial?: boolean;
|
||||||
|
@ -2,16 +2,21 @@ import { Account } from "@/src/models/loginModel";
|
|||||||
import { createInventory } from "@/src/services/inventoryService";
|
import { createInventory } from "@/src/services/inventoryService";
|
||||||
import { IDatabaseAccount, IDatabaseAccountJson } from "@/src/types/loginTypes";
|
import { IDatabaseAccount, IDatabaseAccountJson } from "@/src/types/loginTypes";
|
||||||
import { createShip } from "./shipService";
|
import { createShip } from "./shipService";
|
||||||
import { Types } from "mongoose";
|
import { Document, Types } from "mongoose";
|
||||||
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
|
||||||
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
import { PersonalRooms } from "@/src/models/personalRoomsModel";
|
||||||
import new_personal_rooms from "@/static/fixed_responses/personalRooms.json";
|
import new_personal_rooms from "@/static/fixed_responses/personalRooms.json";
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
|
import { config } from "@/src/services/configService";
|
||||||
|
|
||||||
export const isCorrectPassword = (requestPassword: string, databasePassword: string): boolean => {
|
export const isCorrectPassword = (requestPassword: string, databasePassword: string): boolean => {
|
||||||
return requestPassword === databasePassword;
|
return requestPassword === databasePassword;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isNameTaken = async (name: string): Promise<boolean> => {
|
||||||
|
return !!(await Account.findOne({ DisplayName: name }));
|
||||||
|
};
|
||||||
|
|
||||||
export const createAccount = async (accountData: IDatabaseAccount): Promise<IDatabaseAccountJson> => {
|
export const createAccount = async (accountData: IDatabaseAccount): Promise<IDatabaseAccountJson> => {
|
||||||
const account = new Account(accountData);
|
const account = new Account(accountData);
|
||||||
try {
|
try {
|
||||||
@ -44,20 +49,21 @@ export const createPersonalRooms = async (accountId: Types.ObjectId, shipId: Typ
|
|||||||
await personalRooms.save();
|
await personalRooms.save();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAccountForRequest = async (req: Request) => {
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
type TAccountDocument = Document<unknown, {}, IDatabaseAccountJson> &
|
||||||
|
IDatabaseAccountJson & { _id: Types.ObjectId; __v: number };
|
||||||
|
|
||||||
|
export const getAccountForRequest = async (req: Request): Promise<TAccountDocument> => {
|
||||||
if (!req.query.accountId) {
|
if (!req.query.accountId) {
|
||||||
throw new Error("Request is missing accountId parameter");
|
throw new Error("Request is missing accountId parameter");
|
||||||
}
|
}
|
||||||
if (!req.query.nonce || parseInt(req.query.nonce as string) === 0) {
|
if (!req.query.nonce || parseInt(req.query.nonce as string) === 0) {
|
||||||
throw new Error("Request is missing nonce parameter");
|
throw new Error("Request is missing nonce parameter");
|
||||||
}
|
}
|
||||||
const account = await Account.findOne(
|
const account = await Account.findOne({
|
||||||
{
|
_id: req.query.accountId,
|
||||||
_id: req.query.accountId,
|
Nonce: req.query.nonce
|
||||||
Nonce: req.query.nonce
|
});
|
||||||
},
|
|
||||||
"_id"
|
|
||||||
);
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
throw new Error("Invalid accountId-nonce pair");
|
throw new Error("Invalid accountId-nonce pair");
|
||||||
}
|
}
|
||||||
@ -67,3 +73,7 @@ export const getAccountForRequest = async (req: Request) => {
|
|||||||
export const getAccountIdForRequest = async (req: Request): Promise<string> => {
|
export const getAccountIdForRequest = async (req: Request): Promise<string> => {
|
||||||
return (await getAccountForRequest(req))._id.toString();
|
return (await getAccountForRequest(req))._id.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isAdministrator = (account: TAccountDocument): boolean => {
|
||||||
|
return !!config.administratorNames?.find(x => x == account.DisplayName);
|
||||||
|
};
|
||||||
|
@ -198,75 +198,80 @@
|
|||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
<h5 class="card-header">Server</h5>
|
<h5 class="card-header">Server</h5>
|
||||||
<form class="card-body" onsubmit="doChangeSettings();return false;">
|
<div class="card-body">
|
||||||
<div class="form-check">
|
<div id="server-settings-no-perms" class="d-none">
|
||||||
<input class="form-check-input" type="checkbox" id="skipStoryModeChoice" />
|
<p>You must be an administrator to use this feature. To become an administrator, add <code>"<span class="displayname"></span>"</code> to <code>administratorNames</code> in the config.json.</p>
|
||||||
<label class="form-check-label" for="skipStoryModeChoice">Skip Story Mode Choice</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<form id="server-settings" class="d-none" onsubmit="doChangeSettings();return false;">
|
||||||
<input class="form-check-input" type="checkbox" id="skipTutorial" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="skipTutorial">Skip Tutorial</label>
|
<input class="form-check-input" type="checkbox" id="skipStoryModeChoice" />
|
||||||
</div>
|
<label class="form-check-label" for="skipStoryModeChoice">Skip Story Mode Choice</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="skipAllDialogue" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="skipAllDialogue">Skip All Dialogue</label>
|
<input class="form-check-input" type="checkbox" id="skipTutorial" />
|
||||||
</div>
|
<label class="form-check-label" for="skipTutorial">Skip Tutorial</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="unlockAllScans" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="unlockAllScans">Unlock All Scans</label>
|
<input class="form-check-input" type="checkbox" id="skipAllDialogue" />
|
||||||
</div>
|
<label class="form-check-label" for="skipAllDialogue">Skip All Dialogue</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="unlockAllMissions" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="unlockAllMissions">Unlock All Missions</label>
|
<input class="form-check-input" type="checkbox" id="unlockAllScans" />
|
||||||
</div>
|
<label class="form-check-label" for="unlockAllScans">Unlock All Scans</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="unlockAllQuests" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="unlockAllQuests">Unlock All Quests</label>
|
<input class="form-check-input" type="checkbox" id="unlockAllMissions" />
|
||||||
</div>
|
<label class="form-check-label" for="unlockAllMissions">Unlock All Missions</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="completeAllQuests" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="completeAllQuests">Complete All Quests</label>
|
<input class="form-check-input" type="checkbox" id="unlockAllQuests" />
|
||||||
</div>
|
<label class="form-check-label" for="unlockAllQuests">Unlock All Quests</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="infiniteCredits" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="infiniteCredits">Infinite Credits</label>
|
<input class="form-check-input" type="checkbox" id="completeAllQuests" />
|
||||||
</div>
|
<label class="form-check-label" for="completeAllQuests">Complete All Quests</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="infinitePlatinum" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="infinitePlatinum">Infinite Platinum</label>
|
<input class="form-check-input" type="checkbox" id="infiniteCredits" />
|
||||||
</div>
|
<label class="form-check-label" for="infiniteCredits">Infinite Credits</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="unlockAllShipFeatures">Unlock All Ship Features</label>
|
<input class="form-check-input" type="checkbox" id="infinitePlatinum" />
|
||||||
</div>
|
<label class="form-check-label" for="infinitePlatinum">Infinite Platinum</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="unlockAllShipDecorations" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="unlockAllShipDecorations">Unlock All Ship Decorations</label>
|
<input class="form-check-input" type="checkbox" id="unlockAllShipFeatures" />
|
||||||
</div>
|
<label class="form-check-label" for="unlockAllShipFeatures">Unlock All Ship Features</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="unlockAllFlavourItems" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="unlockAllFlavourItems">
|
<input class="form-check-input" type="checkbox" id="unlockAllShipDecorations" />
|
||||||
Unlock All <abbr title="Animation Sets, Glyphs, Plattes, etc.">Flavor Items</abbr>
|
<label class="form-check-label" for="unlockAllShipDecorations">Unlock All Ship Decorations</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
<div class="form-check">
|
||||||
<div class="form-check">
|
<input class="form-check-input" type="checkbox" id="unlockAllFlavourItems" />
|
||||||
<input class="form-check-input" type="checkbox" id="unlockAllSkins" />
|
<label class="form-check-label" for="unlockAllFlavourItems">
|
||||||
<label class="form-check-label" for="unlockAllSkins">Unlock All Skins</label>
|
Unlock All <abbr title="Animation Sets, Glyphs, Plattes, etc.">Flavor Items</abbr>
|
||||||
</div>
|
</label>
|
||||||
<div class="form-check">
|
</div>
|
||||||
<input class="form-check-input" type="checkbox" id="universalPolarityEverywhere" />
|
<div class="form-check">
|
||||||
<label class="form-check-label" for="universalPolarityEverywhere">
|
<input class="form-check-input" type="checkbox" id="unlockAllSkins" />
|
||||||
Universal Polarity Everywhere
|
<label class="form-check-label" for="unlockAllSkins">Unlock All Skins</label>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
<div class="form-check">
|
||||||
<div class="form-group mt-2">
|
<input class="form-check-input" type="checkbox" id="universalPolarityEverywhere" />
|
||||||
<label class="form-label" for="spoofMasteryRank">
|
<label class="form-check-label" for="universalPolarityEverywhere">
|
||||||
Spoofed Mastery Rank (-1 to disable)
|
Universal Polarity Everywhere
|
||||||
</label>
|
</label>
|
||||||
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" />
|
</div>
|
||||||
</div>
|
<div class="form-group mt-2">
|
||||||
<button class="btn btn-primary mt-3" type="submit">Save Settings</button>
|
<label class="form-label" for="spoofMasteryRank">
|
||||||
</form>
|
Spoofed Mastery Rank (-1 to disable)
|
||||||
|
</label>
|
||||||
|
<input class="form-control" id="spoofMasteryRank" type="number" min="-1" max="65535" />
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary mt-3" type="submit">Save Settings</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-4">
|
<div class="col-lg-4">
|
||||||
|
@ -792,7 +792,7 @@ const uiConfigs = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function doChangeSettings() {
|
function doChangeSettings() {
|
||||||
fetch("/custom/config")
|
fetch("/custom/config?" + window.authz)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(json => {
|
.then(json => {
|
||||||
for (const i of uiConfigs) {
|
for (const i of uiConfigs) {
|
||||||
@ -810,7 +810,7 @@ function doChangeSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$.post({
|
$.post({
|
||||||
url: "/custom/config",
|
url: "/custom/config?" + window.authz,
|
||||||
contentType: "text/plain",
|
contentType: "text/plain",
|
||||||
data: JSON.stringify(json, null, 2)
|
data: JSON.stringify(json, null, 2)
|
||||||
});
|
});
|
||||||
@ -820,23 +820,34 @@ function doChangeSettings() {
|
|||||||
// Cheats route
|
// Cheats route
|
||||||
|
|
||||||
single.getRoute("/webui/cheats").on("beforeload", function () {
|
single.getRoute("/webui/cheats").on("beforeload", function () {
|
||||||
fetch("/custom/config")
|
let interval;
|
||||||
.then(response => response.json())
|
interval = setInterval(() => {
|
||||||
.then(json =>
|
if (window.authz) {
|
||||||
Object.entries(json).forEach(entry => {
|
clearInterval(interval);
|
||||||
const [key, value] = entry;
|
fetch("/custom/config?" + window.authz).then(res => {
|
||||||
var x = document.getElementById(`${key}`);
|
if (res.status == 200) {
|
||||||
if (x != null) {
|
$("#server-settings").removeClass("d-none");
|
||||||
if (x.type == "checkbox") {
|
res.json().then(json =>
|
||||||
if (value === true) {
|
Object.entries(json).forEach(entry => {
|
||||||
x.setAttribute("checked", "checked");
|
const [key, value] = entry;
|
||||||
}
|
var x = document.getElementById(`${key}`);
|
||||||
} else if (x.type == "number") {
|
if (x != null) {
|
||||||
x.setAttribute("value", `${value}`);
|
if (x.type == "checkbox") {
|
||||||
}
|
if (value === true) {
|
||||||
|
x.setAttribute("checked", "checked");
|
||||||
|
}
|
||||||
|
} else if (x.type == "number") {
|
||||||
|
x.setAttribute("value", `${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$("#server-settings-no-perms").removeClass("d-none");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
);
|
}
|
||||||
|
}, 10);
|
||||||
|
|
||||||
fetch("http://localhost:61558/ping", { mode: "no-cors" })
|
fetch("http://localhost:61558/ping", { mode: "no-cors" })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user