Compare commits

...

4 Commits

Author SHA1 Message Date
173a4791d9 Upload files to "static/webui" 2025-03-23 10:14:22 -07:00
8a29f06207 chore: use inventory projection for updateTheme (#1302)
Reviewed-on: OpenWF/SpaceNinjaServer#1302
2025-03-23 09:06:28 -07:00
cf3007b744 chore: update config when admin changes their name (#1298)
Reviewed-on: OpenWF/SpaceNinjaServer#1298
2025-03-23 09:06:08 -07:00
7f5592e00c chore: improve authentication and Dropped logic (#1296)
- Dropped is now also unset by getAccountForRequest
- Improved how nonce is validated to avoid possible parser mismatch issues to smuggle a 0
- Updated ircDroppedController to perform only a single MongoDB operation

Reviewed-on: OpenWF/SpaceNinjaServer#1296
2025-03-23 09:05:47 -07:00
9 changed files with 68167 additions and 62 deletions

View File

@ -1,25 +1,23 @@
import { getAccountIdForRequest } from "@/src/services/loginService"; import { getAccountIdForRequest } from "@/src/services/loginService";
import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getJSONfromString } from "@/src/helpers/stringHelpers";
import { updateTheme } from "@/src/services/inventoryService";
import { IThemeUpdateRequest } from "@/src/types/requestTypes";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getInventory } from "@/src/services/inventoryService";
const updateThemeController: RequestHandler = async (request, response) => { export const updateThemeController: RequestHandler = async (request, response) => {
const accountId = await getAccountIdForRequest(request); const accountId = await getAccountIdForRequest(request);
const body = String(request.body); const data = getJSONfromString<IThemeUpdateRequest>(String(request.body));
try { const inventory = await getInventory(accountId, "ThemeStyle ThemeBackground ThemeSounds");
const json = getJSONfromString<IThemeUpdateRequest>(body); if (data.Style) inventory.ThemeStyle = data.Style;
if (typeof json !== "object") { if (data.Background) inventory.ThemeBackground = data.Background;
throw new Error("Invalid data format"); if (data.Sounds) inventory.ThemeSounds = data.Sounds;
} await inventory.save();
await updateTheme(json, accountId);
} catch (err) {
console.error("Error parsing JSON data:", err);
}
response.json({}); response.json({});
}; };
export { updateThemeController }; interface IThemeUpdateRequest {
Style?: string;
Background?: string;
Sounds?: string;
}

View File

@ -1,9 +1,24 @@
import { getAccountForRequest } from "@/src/services/loginService"; import { Account } from "@/src/models/loginModel";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
export const ircDroppedController: RequestHandler = async (req, res) => { export const ircDroppedController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req); if (!req.query.accountId) {
account.Dropped = true; throw new Error("Request is missing accountId parameter");
await account.save(); }
const nonce: number = parseInt(req.query.nonce as string);
if (!nonce) {
throw new Error("Request is missing nonce parameter");
}
await Account.updateOne(
{
_id: req.query.accountId,
Nonce: nonce
},
{
Dropped: true
}
);
res.end(); res.end();
}; };

View File

@ -1,5 +1,6 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getAccountForRequest, isNameTaken } from "@/src/services/loginService"; import { getAccountForRequest, isAdministrator, isNameTaken } from "@/src/services/loginService";
import { config, saveConfig } from "@/src/services/configService";
export const renameAccountController: RequestHandler = async (req, res) => { export const renameAccountController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req); const account = await getAccountForRequest(req);
@ -7,8 +8,18 @@ export const renameAccountController: RequestHandler = async (req, res) => {
if (await isNameTaken(req.query.newname)) { if (await isNameTaken(req.query.newname)) {
res.status(409).json("Name already in use"); res.status(409).json("Name already in use");
} else { } else {
if (isAdministrator(account)) {
for (let i = 0; i != config.administratorNames!.length; ++i) {
if (config.administratorNames![i] == account.DisplayName) {
config.administratorNames![i] = req.query.newname;
}
}
await saveConfig();
}
account.DisplayName = req.query.newname; account.DisplayName = req.query.newname;
await account.save(); await account.save();
res.end(); res.end();
} }
} else { } else {

View File

@ -19,9 +19,13 @@ import mongoose from "mongoose";
return "<BIGINT>" + this.toString() + "</BIGINT>"; return "<BIGINT>" + this.toString() + "</BIGINT>";
}; };
const og_stringify = JSON.stringify; const og_stringify = JSON.stringify;
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access // eslint-disable-next-line @typescript-eslint/no-explicit-any
(JSON as any).stringify = (obj: any): string => { JSON.stringify = (obj: any, replacer?: any, space?: string | number): string => {
return og_stringify(obj).split(`"<BIGINT>`).join(``).split(`</BIGINT>"`).join(``); return og_stringify(obj, replacer as string[], space)
.split(`"<BIGINT>`)
.join(``)
.split(`</BIGINT>"`)
.join(``);
}; };
} }

View File

@ -34,7 +34,7 @@ interface IConfig {
httpsPort?: number; httpsPort?: number;
myIrcAddresses?: string[]; myIrcAddresses?: string[];
NRS?: string[]; NRS?: string[];
administratorNames?: string[] | string; administratorNames?: string[];
autoCreateAccount?: boolean; autoCreateAccount?: boolean;
skipTutorial?: boolean; skipTutorial?: boolean;
skipAllDialogue?: boolean; skipAllDialogue?: boolean;
@ -83,10 +83,15 @@ export const updateConfig = async (data: string): Promise<void> => {
Object.assign(config, JSON.parse(data)); Object.assign(config, JSON.parse(data));
}; };
export const saveConfig = async (): Promise<void> => {
amnesia = true;
await fsPromises.writeFile(configPath, JSON.stringify(config, null, 2));
};
export const validateConfig = (): void => { export const validateConfig = (): void => {
if (typeof config.administratorNames == "string") { if (typeof config.administratorNames == "string") {
logger.warn( logger.info(`Updating config.json to make administratorNames an array.`);
`"administratorNames" should be an array; please add square brackets: ["${config.administratorNames}"]` config.administratorNames = [config.administratorNames];
); void saveConfig();
} }
}; };

View File

@ -29,11 +29,7 @@ import {
ICrewShipWeaponClient ICrewShipWeaponClient
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate"; import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate";
import { import { IMissionInventoryUpdateRequest, IUpdateChallengeProgressRequest } from "../types/requestTypes";
IMissionInventoryUpdateRequest,
IThemeUpdateRequest,
IUpdateChallengeProgressRequest
} from "../types/requestTypes";
import { logger } from "@/src/utils/logger"; import { logger } from "@/src/utils/logger";
import { convertInboxMessage, fromStoreItem, getExalted, getKeyChainItems } from "@/src/services/itemDataService"; import { convertInboxMessage, fromStoreItem, getExalted, getKeyChainItems } from "@/src/services/itemDataService";
import { import {
@ -891,15 +887,6 @@ export const updateGeneric = async (data: IGenericUpdate, accountId: string): Pr
}; };
}; };
export const updateTheme = async (data: IThemeUpdateRequest, accountId: string): Promise<void> => {
const inventory = await getInventory(accountId);
if (data.Style) inventory.ThemeStyle = data.Style;
if (data.Background) inventory.ThemeBackground = data.Background;
if (data.Sounds) inventory.ThemeSounds = data.Sounds;
await inventory.save();
};
export const addEquipment = ( export const addEquipment = (
inventory: TInventoryDatabaseDocument, inventory: TInventoryDatabaseDocument,
category: TEquipmentKey, category: TEquipmentKey,

View File

@ -69,36 +69,31 @@ export const getAccountForRequest = async (req: Request): Promise<TAccountDocume
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) { const nonce: number = parseInt(req.query.nonce as string);
if (!nonce) {
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: nonce
}); });
if (!account) { if (!account) {
throw new Error("Invalid accountId-nonce pair"); throw new Error("Invalid accountId-nonce pair");
} }
if (account.Dropped && req.query.ct) {
account.Dropped = undefined;
await account.save();
}
return account; return account;
}; };
export const getAccountIdForRequest = async (req: Request): Promise<string> => { export const getAccountIdForRequest = async (req: Request): Promise<string> => {
const account = await getAccountForRequest(req); return (await getAccountForRequest(req))._id.toString();
if (account.Dropped && req.query.ct) {
account.Dropped = undefined;
await account.save();
}
return account._id.toString();
}; };
export const isAdministrator = (account: TAccountDocument): boolean => { export const isAdministrator = (account: TAccountDocument): boolean => {
if (!config.administratorNames) { return !!config.administratorNames?.find(x => x == account.DisplayName);
return false;
}
if (typeof config.administratorNames == "string") {
return config.administratorNames == account.DisplayName;
}
return !!config.administratorNames.find(x => x == account.DisplayName);
}; };
const platform_magics = [753, 639, 247, 37, 60]; const platform_magics = [753, 639, 247, 37, 60];

View File

@ -19,12 +19,6 @@ import {
ICollectibleEntry ICollectibleEntry
} from "./inventoryTypes/inventoryTypes"; } from "./inventoryTypes/inventoryTypes";
export interface IThemeUpdateRequest {
Style?: string;
Background?: string;
Sounds?: string;
}
export interface IAffiliationChange { export interface IAffiliationChange {
Tag: string; Tag: string;
Standing: number; Standing: number;

68096
static/webui/inventory.json Normal file

File diff suppressed because it is too large Load Diff