Compare commits

..

1 Commits

Author SHA1 Message Date
0949299c3f chore: add custom getAccountInfo endpoint
This will help the IRC server get all the information it needs for permission management in a single request.
2025-03-23 14:29:51 +01:00
16 changed files with 225 additions and 68244 deletions

View File

@ -3,7 +3,6 @@
echo Updating SpaceNinjaServer... echo Updating SpaceNinjaServer...
git config remote.origin.url https://openwf.io/SpaceNinjaServer.git git config remote.origin.url https://openwf.io/SpaceNinjaServer.git
git fetch --prune git fetch --prune
git stash
git reset --hard origin/main git reset --hard origin/main
if exist static\data\0\ ( if exist static\data\0\ (

View File

@ -118,7 +118,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) =
} }
InventoryChanges = { InventoryChanges = {
...InventoryChanges, ...InventoryChanges,
...(await addItem(inventory, recipe.resultType, recipe.num, false)) ...(await addItem(inventory, recipe.resultType, recipe.num, false)).InventoryChanges
}; };
await inventory.save(); await inventory.save();
res.json({ InventoryChanges }); res.json({ InventoryChanges });

View File

@ -87,7 +87,7 @@ export const addStartingGear = async (
for (const item of awakeningRewards) { for (const item of awakeningRewards) {
const inventoryDelta = await addItem(inventory, item); const inventoryDelta = await addItem(inventory, item);
combineInventoryChanges(inventoryChanges, inventoryDelta); combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
} }
inventory.PlayedParkourTutorial = true; inventory.PlayedParkourTutorial = true;

View File

@ -194,7 +194,7 @@ export const guildTechController: RequestHandler = async (req, res) => {
ItemCount: x.ItemCount * -1 ItemCount: x.ItemCount * -1
})); }));
addMiscItems(inventory, inventoryChanges.MiscItems); addMiscItems(inventory, inventoryChanges.MiscItems);
combineInventoryChanges(inventoryChanges, await addItem(inventory, recipe.resultType)); combineInventoryChanges(inventoryChanges, (await addItem(inventory, recipe.resultType)).InventoryChanges);
await inventory.save(); await inventory.save();
// Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`. // Not a mistake: This response uses `inventoryChanges` instead of `InventoryChanges`.
res.json({ inventoryChanges: inventoryChanges }); res.json({ inventoryChanges: inventoryChanges });

View File

@ -1,23 +1,25 @@
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";
export const updateThemeController: RequestHandler = async (request, response) => { const updateThemeController: RequestHandler = async (request, response) => {
const accountId = await getAccountIdForRequest(request); const accountId = await getAccountIdForRequest(request);
const data = getJSONfromString<IThemeUpdateRequest>(String(request.body)); const body = String(request.body);
const inventory = await getInventory(accountId, "ThemeStyle ThemeBackground ThemeSounds"); try {
if (data.Style) inventory.ThemeStyle = data.Style; const json = getJSONfromString<IThemeUpdateRequest>(body);
if (data.Background) inventory.ThemeBackground = data.Background; if (typeof json !== "object") {
if (data.Sounds) inventory.ThemeSounds = data.Sounds; throw new Error("Invalid data format");
await inventory.save(); }
await updateTheme(json, accountId);
} catch (err) {
console.error("Error parsing JSON data:", err);
}
response.json({}); response.json({});
}; };
interface IThemeUpdateRequest { export { updateThemeController };
Style?: string;
Background?: string;
Sounds?: string;
}

View File

@ -0,0 +1,27 @@
import { Guild, GuildMember } from "@/src/models/guildModel";
import { getAccountForRequest, isAdministrator } from "@/src/services/loginService";
import { RequestHandler } from "express";
export const getAccountInfoController: RequestHandler = async (req, res) => {
const account = await getAccountForRequest(req);
const info: IAccountInfo = {
DisplayName: account.DisplayName
};
if (isAdministrator(account)) {
info.IsAdministrator = true;
}
const guildMember = await GuildMember.findOne({ accountId: account._id, status: 0 }, "guildId rank");
if (guildMember) {
const guild = (await Guild.findOne({ _id: guildMember.guildId }, "Ranks"))!;
info.GuildId = guildMember.guildId.toString();
info.GuildPermissions = guild.Ranks[guildMember.rank].Permissions;
}
res.json(info);
};
interface IAccountInfo {
DisplayName: string;
IsAdministrator?: boolean;
GuildId?: string;
GuildPermissions?: number;
}

View File

@ -1,24 +1,9 @@
import { Account } from "@/src/models/loginModel"; import { getAccountForRequest } from "@/src/services/loginService";
import { RequestHandler } from "express"; import { RequestHandler } from "express";
export const ircDroppedController: RequestHandler = async (req, res) => { export const ircDroppedController: RequestHandler = async (req, res) => {
if (!req.query.accountId) { const account = await getAccountForRequest(req);
throw new Error("Request is missing accountId parameter"); account.Dropped = true;
} 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,6 +1,5 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getAccountForRequest, isAdministrator, isNameTaken } from "@/src/services/loginService"; import { getAccountForRequest, 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);
@ -8,18 +7,8 @@ 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,13 +19,9 @@ 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 // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
JSON.stringify = (obj: any, replacer?: any, space?: string | number): string => { (JSON as any).stringify = (obj: any): string => {
return og_stringify(obj, replacer as string[], space) return og_stringify(obj).split(`"<BIGINT>`).join(``).split(`</BIGINT>"`).join(``);
.split(`"<BIGINT>`)
.join(``)
.split(`</BIGINT>"`)
.join(``);
}; };
} }

View File

@ -6,6 +6,7 @@ import { pushArchonCrystalUpgradeController } from "@/src/controllers/custom/pus
import { popArchonCrystalUpgradeController } from "@/src/controllers/custom/popArchonCrystalUpgradeController"; import { popArchonCrystalUpgradeController } from "@/src/controllers/custom/popArchonCrystalUpgradeController";
import { deleteAccountController } from "@/src/controllers/custom/deleteAccountController"; import { deleteAccountController } from "@/src/controllers/custom/deleteAccountController";
import { getNameController } from "@/src/controllers/custom/getNameController"; import { getNameController } from "@/src/controllers/custom/getNameController";
import { getAccountInfoController } from "@/src/controllers/custom/getAccountInfoController";
import { renameAccountController } from "@/src/controllers/custom/renameAccountController"; import { renameAccountController } from "@/src/controllers/custom/renameAccountController";
import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController"; import { ircDroppedController } from "@/src/controllers/custom/ircDroppedController";
import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController"; import { unlockAllIntrinsicsController } from "@/src/controllers/custom/unlockAllIntrinsicsController";
@ -29,6 +30,7 @@ customRouter.get("/pushArchonCrystalUpgrade", pushArchonCrystalUpgradeController
customRouter.get("/popArchonCrystalUpgrade", popArchonCrystalUpgradeController); customRouter.get("/popArchonCrystalUpgrade", popArchonCrystalUpgradeController);
customRouter.get("/deleteAccount", deleteAccountController); customRouter.get("/deleteAccount", deleteAccountController);
customRouter.get("/getName", getNameController); customRouter.get("/getName", getNameController);
customRouter.get("/getAccountInfo", getAccountInfoController);
customRouter.get("/renameAccount", renameAccountController); customRouter.get("/renameAccount", renameAccountController);
customRouter.get("/ircDropped", ircDroppedController); customRouter.get("/ircDropped", ircDroppedController);
customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController); customRouter.get("/unlockAllIntrinsics", unlockAllIntrinsicsController);

View File

@ -34,7 +34,7 @@ interface IConfig {
httpsPort?: number; httpsPort?: number;
myIrcAddresses?: string[]; myIrcAddresses?: string[];
NRS?: string[]; NRS?: string[];
administratorNames?: string[]; administratorNames?: string[] | string;
autoCreateAccount?: boolean; autoCreateAccount?: boolean;
skipTutorial?: boolean; skipTutorial?: boolean;
skipAllDialogue?: boolean; skipAllDialogue?: boolean;
@ -83,15 +83,10 @@ 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.info(`Updating config.json to make administratorNames an array.`); logger.warn(
config.administratorNames = [config.administratorNames]; `"administratorNames" should be an array; please add square brackets: ["${config.administratorNames}"]`
void saveConfig(); );
} }
}; };

View File

@ -29,7 +29,11 @@ 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 { IMissionInventoryUpdateRequest, IUpdateChallengeProgressRequest } from "../types/requestTypes"; import {
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 {
@ -230,10 +234,10 @@ export const addItem = async (
typeName: string, typeName: string,
quantity: number = 1, quantity: number = 1,
premiumPurchase: boolean = false premiumPurchase: boolean = false
): Promise<IInventoryChanges> => { ): Promise<{ InventoryChanges: IInventoryChanges }> => {
// Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments. // Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments.
if (typeName in ExportBundles) { if (typeName in ExportBundles) {
return await handleBundleAcqusition(typeName, inventory, quantity); return { InventoryChanges: await handleBundleAcqusition(typeName, inventory, quantity) };
} }
// Strict typing // Strict typing
@ -246,7 +250,9 @@ export const addItem = async (
]; ];
addRecipes(inventory, recipeChanges); addRecipes(inventory, recipeChanges);
return { return {
Recipes: recipeChanges InventoryChanges: {
Recipes: recipeChanges
}
}; };
} }
if (typeName in ExportResources) { if (typeName in ExportResources) {
@ -259,7 +265,9 @@ export const addItem = async (
]; ];
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
return { return {
MiscItems: miscItemChanges InventoryChanges: {
MiscItems: miscItemChanges
}
}; };
} else if (ExportResources[typeName].productCategory == "FusionTreasures") { } else if (ExportResources[typeName].productCategory == "FusionTreasures") {
const fusionTreasureChanges = [ const fusionTreasureChanges = [
@ -271,21 +279,25 @@ export const addItem = async (
]; ];
addFusionTreasures(inventory, fusionTreasureChanges); addFusionTreasures(inventory, fusionTreasureChanges);
return { return {
FusionTreasures: fusionTreasureChanges InventoryChanges: {
FusionTreasures: fusionTreasureChanges
}
}; };
} else if (ExportResources[typeName].productCategory == "Ships") { } else if (ExportResources[typeName].productCategory == "Ships") {
const oid = await createShip(inventory.accountOwnerId, typeName); const oid = await createShip(inventory.accountOwnerId, typeName);
inventory.Ships.push(oid); inventory.Ships.push(oid);
return { return {
Ships: [ InventoryChanges: {
{ Ships: [
ItemId: { $oid: oid.toString() }, {
ItemType: typeName ItemId: { $oid: oid.toString() },
} ItemType: typeName
] }
]
}
}; };
} else if (ExportResources[typeName].productCategory == "CrewShips") { } else if (ExportResources[typeName].productCategory == "CrewShips") {
return { const inventoryChanges = {
...addCrewShip(inventory, typeName), ...addCrewShip(inventory, typeName),
// fix to unlock railjack modding, item bellow supposed to be obtained from archwing quest // fix to unlock railjack modding, item bellow supposed to be obtained from archwing quest
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@ -293,6 +305,8 @@ export const addItem = async (
? addCrewShipHarness(inventory, "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness") ? addCrewShipHarness(inventory, "/Lotus/Types/Game/CrewShip/RailJack/DefaultHarness")
: {}) : {})
}; };
return { InventoryChanges: inventoryChanges };
} else if (ExportResources[typeName].productCategory == "ShipDecorations") { } else if (ExportResources[typeName].productCategory == "ShipDecorations") {
const changes = [ const changes = [
{ {
@ -302,7 +316,9 @@ export const addItem = async (
]; ];
addShipDecorations(inventory, changes); addShipDecorations(inventory, changes);
return { return {
ShipDecorations: changes InventoryChanges: {
ShipDecorations: changes
}
}; };
} else if (ExportResources[typeName].productCategory == "KubrowPetEggs") { } else if (ExportResources[typeName].productCategory == "KubrowPetEggs") {
const changes: IKubrowPetEggClient[] = []; const changes: IKubrowPetEggClient[] = [];
@ -323,7 +339,9 @@ export const addItem = async (
}); });
} }
return { return {
KubrowPetEggs: changes InventoryChanges: {
KubrowPetEggs: changes
}
}; };
} else { } else {
throw new Error(`unknown product category: ${ExportResources[typeName].productCategory}`); throw new Error(`unknown product category: ${ExportResources[typeName].productCategory}`);
@ -331,13 +349,14 @@ export const addItem = async (
} }
if (typeName in ExportCustoms) { if (typeName in ExportCustoms) {
if (ExportCustoms[typeName].productCategory == "CrewShipWeaponSkins") { if (ExportCustoms[typeName].productCategory == "CrewShipWeaponSkins") {
return addCrewShipWeaponSkin(inventory, typeName); return { InventoryChanges: addCrewShipWeaponSkin(inventory, typeName) };
} else { } else {
return addSkin(inventory, typeName); return { InventoryChanges: addSkin(inventory, typeName) };
} }
} }
if (typeName in ExportFlavour) { if (typeName in ExportFlavour) {
return addCustomization(inventory, typeName); const inventoryChanges = addCustomization(inventory, typeName);
return { InventoryChanges: inventoryChanges };
} }
if (typeName in ExportUpgrades || typeName in ExportArcanes) { if (typeName in ExportUpgrades || typeName in ExportArcanes) {
const changes = [ const changes = [
@ -348,7 +367,9 @@ export const addItem = async (
]; ];
addMods(inventory, changes); addMods(inventory, changes);
return { return {
RawUpgrades: changes InventoryChanges: {
RawUpgrades: changes
}
}; };
} }
if (typeName in ExportGear) { if (typeName in ExportGear) {
@ -360,7 +381,9 @@ export const addItem = async (
]; ];
addConsumables(inventory, consumablesChanges); addConsumables(inventory, consumablesChanges);
return { return {
Consumables: consumablesChanges InventoryChanges: {
Consumables: consumablesChanges
}
}; };
} }
if (typeName in ExportWeapons) { if (typeName in ExportWeapons) {
@ -403,12 +426,14 @@ export const addItem = async (
); );
if (weapon.additionalItems) { if (weapon.additionalItems) {
for (const item of weapon.additionalItems) { for (const item of weapon.additionalItems) {
combineInventoryChanges(inventoryChanges, await addItem(inventory, item, 1)); combineInventoryChanges(inventoryChanges, (await addItem(inventory, item, 1)).InventoryChanges);
} }
} }
return { return {
...inventoryChanges, InventoryChanges: {
...occupySlot(inventory, InventorySlot.WEAPONS, premiumPurchase) ...inventoryChanges,
...occupySlot(inventory, InventorySlot.WEAPONS, premiumPurchase)
}
}; };
} else { } else {
// Modular weapon parts // Modular weapon parts
@ -420,28 +445,36 @@ export const addItem = async (
]; ];
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
return { return {
MiscItems: miscItemChanges InventoryChanges: {
MiscItems: miscItemChanges
}
}; };
} }
} }
if (typeName in ExportRailjackWeapons) { if (typeName in ExportRailjackWeapons) {
return { return {
...addCrewShipWeapon(inventory, typeName), InventoryChanges: {
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, premiumPurchase) ...addCrewShipWeapon(inventory, typeName),
...occupySlot(inventory, InventorySlot.RJ_COMPONENT_AND_ARMAMENTS, premiumPurchase)
}
}; };
} }
if (typeName in ExportMisc.creditBundles) { if (typeName in ExportMisc.creditBundles) {
const creditsTotal = ExportMisc.creditBundles[typeName] * quantity; const creditsTotal = ExportMisc.creditBundles[typeName] * quantity;
inventory.RegularCredits += creditsTotal; inventory.RegularCredits += creditsTotal;
return { return {
RegularCredits: creditsTotal InventoryChanges: {
RegularCredits: creditsTotal
}
}; };
} }
if (typeName in ExportFusionBundles) { if (typeName in ExportFusionBundles) {
const fusionPointsTotal = ExportFusionBundles[typeName].fusionPoints * quantity; const fusionPointsTotal = ExportFusionBundles[typeName].fusionPoints * quantity;
inventory.FusionPoints += fusionPointsTotal; inventory.FusionPoints += fusionPointsTotal;
return { return {
FusionPoints: fusionPointsTotal InventoryChanges: {
FusionPoints: fusionPointsTotal
}
}; };
} }
if (typeName in ExportKeys) { if (typeName in ExportKeys) {
@ -450,8 +483,8 @@ export const addItem = async (
if (key.chainStages) { if (key.chainStages) {
const key = addQuestKey(inventory, { ItemType: typeName }); const key = addQuestKey(inventory, { ItemType: typeName });
if (!key) return {}; if (!key) return { InventoryChanges: {} };
return { QuestKeys: [key] }; return { InventoryChanges: { QuestKeys: [key] } };
} else { } else {
const key = { ItemType: typeName, ItemCount: quantity }; const key = { ItemType: typeName, ItemCount: quantity };
@ -461,14 +494,19 @@ export const addItem = async (
} else { } else {
inventory.LevelKeys.push(key); inventory.LevelKeys.push(key);
} }
return { LevelKeys: [key] }; return { InventoryChanges: { LevelKeys: [key] } };
} }
} }
if (typeName in ExportDrones) { if (typeName in ExportDrones) {
return addDrone(inventory, typeName); const inventoryChanges = addDrone(inventory, typeName);
return {
InventoryChanges: inventoryChanges
};
} }
if (typeName in ExportEmailItems) { if (typeName in ExportEmailItems) {
return await addEmailItem(inventory, typeName); return {
InventoryChanges: await addEmailItem(inventory, typeName)
};
} }
// Path-based duck typing // Path-based duck typing
@ -477,36 +515,42 @@ export const addItem = async (
switch (typeName.substr(1).split("/")[2]) { switch (typeName.substr(1).split("/")[2]) {
default: { default: {
return { return {
...addPowerSuit( InventoryChanges: {
inventory, ...addPowerSuit(
typeName, inventory,
{}, typeName,
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined {},
), premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase) ),
...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase)
}
}; };
} }
case "Archwing": { case "Archwing": {
inventory.ArchwingEnabled = true; inventory.ArchwingEnabled = true;
return { return {
...addSpaceSuit( InventoryChanges: {
inventory, ...addSpaceSuit(
typeName, inventory,
{}, typeName,
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined {},
), premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
...occupySlot(inventory, InventorySlot.SPACESUITS, premiumPurchase) ),
...occupySlot(inventory, InventorySlot.SPACESUITS, premiumPurchase)
}
}; };
} }
case "EntratiMech": { case "EntratiMech": {
return { return {
...addMechSuit( InventoryChanges: {
inventory, ...addMechSuit(
typeName, inventory,
{}, typeName,
premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined {},
), premiumPurchase ? EquipmentFeatures.DOUBLE_CAPACITY : undefined
...occupySlot(inventory, InventorySlot.MECHSUITS, premiumPurchase) ),
...occupySlot(inventory, InventorySlot.MECHSUITS, premiumPurchase)
}
}; };
} }
} }
@ -524,7 +568,9 @@ export const addItem = async (
]; ];
addMods(inventory, changes); addMods(inventory, changes);
return { return {
RawUpgrades: changes InventoryChanges: {
RawUpgrades: changes
}
}; };
} }
break; break;
@ -541,7 +587,9 @@ export const addItem = async (
]; ];
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
return { return {
MiscItems: miscItemChanges InventoryChanges: {
MiscItems: miscItemChanges
}
}; };
} else { } else {
const changes = [ const changes = [
@ -552,7 +600,9 @@ export const addItem = async (
]; ];
addMods(inventory, changes); addMods(inventory, changes);
return { return {
RawUpgrades: changes InventoryChanges: {
RawUpgrades: changes
}
}; };
} }
} }
@ -563,7 +613,9 @@ export const addItem = async (
case "Types": case "Types":
switch (typeName.substr(1).split("/")[2]) { switch (typeName.substr(1).split("/")[2]) {
case "Sentinels": { case "Sentinels": {
return addSentinel(inventory, typeName, premiumPurchase); return {
InventoryChanges: addSentinel(inventory, typeName, premiumPurchase)
};
} }
case "Game": { case "Game": {
if (typeName.substr(1).split("/")[3] == "Projections") { if (typeName.substr(1).split("/")[3] == "Projections") {
@ -577,7 +629,9 @@ export const addItem = async (
addMiscItems(inventory, miscItemChanges); addMiscItems(inventory, miscItemChanges);
inventory.HasOwnedVoidProjectionsPreviously = true; inventory.HasOwnedVoidProjectionsPreviously = true;
return { return {
MiscItems: miscItemChanges InventoryChanges: {
MiscItems: miscItemChanges
}
}; };
} }
break; break;
@ -585,23 +639,27 @@ export const addItem = async (
case "NeutralCreatures": { case "NeutralCreatures": {
const horseIndex = inventory.Horses.push({ ItemType: typeName }); const horseIndex = inventory.Horses.push({ ItemType: typeName });
return { return {
Horses: [inventory.Horses[horseIndex - 1].toJSON<IEquipmentClient>()] InventoryChanges: {
Horses: [inventory.Horses[horseIndex - 1].toJSON<IEquipmentClient>()]
}
}; };
} }
case "Recipes": { case "Recipes": {
inventory.MiscItems.push({ ItemType: typeName, ItemCount: quantity }); inventory.MiscItems.push({ ItemType: typeName, ItemCount: quantity });
return { return {
MiscItems: [ InventoryChanges: {
{ MiscItems: [
ItemType: typeName, {
ItemCount: quantity ItemType: typeName,
} ItemCount: quantity
] }
]
}
}; };
} }
case "Vehicles": case "Vehicles":
if (typeName == "/Lotus/Types/Vehicles/Motorcycle/MotorcyclePowerSuit") { if (typeName == "/Lotus/Types/Vehicles/Motorcycle/MotorcyclePowerSuit") {
return addMotorcycle(inventory, typeName); return { InventoryChanges: addMotorcycle(inventory, typeName) };
} }
break; break;
} }
@ -622,7 +680,7 @@ export const addItems = async (
} else { } else {
inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount, true); inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount, true);
} }
combineInventoryChanges(inventoryChanges, inventoryDelta); combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges);
} }
return inventoryChanges; return inventoryChanges;
}; };
@ -887,6 +945,15 @@ 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,
@ -1321,11 +1388,12 @@ export const addKeyChainItems = async (
const nonStoreItems = keyChainItems.map(item => fromStoreItem(item)); const nonStoreItems = keyChainItems.map(item => fromStoreItem(item));
const inventoryChanges: IInventoryChanges = {}; //TODO: inventoryChanges is not typed correctly
const inventoryChanges = {};
for (const item of nonStoreItems) { for (const item of nonStoreItems) {
const inventoryChangesDelta = await addItem(inventory, item); const inventoryChangesDelta = await addItem(inventory, item);
combineInventoryChanges(inventoryChanges, inventoryChangesDelta); combineInventoryChanges(inventoryChanges, inventoryChangesDelta.InventoryChanges);
} }
return inventoryChanges; return inventoryChanges;

View File

@ -69,31 +69,36 @@ 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");
} }
const nonce: number = parseInt(req.query.nonce as string); if (!req.query.nonce || parseInt(req.query.nonce as string) === 0) {
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: nonce Nonce: req.query.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> => {
return (await getAccountForRequest(req))._id.toString(); const account = await getAccountForRequest(req);
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 => {
return !!config.administratorNames?.find(x => x == account.DisplayName); if (!config.administratorNames) {
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

@ -333,7 +333,7 @@ export const handleStoreItemAcquisition = async (
} }
switch (storeCategory) { switch (storeCategory) {
default: { default: {
purchaseResponse = { InventoryChanges: await addItem(inventory, internalName, quantity, true) }; purchaseResponse = await addItem(inventory, internalName, quantity, true);
break; break;
} }
case "Types": case "Types":
@ -418,7 +418,10 @@ const handleBoosterPackPurchase = async (
if (typeName == "/Lotus/Types/BoosterPacks/1999StickersPackEchoesArchimedeaFixed") { if (typeName == "/Lotus/Types/BoosterPacks/1999StickersPackEchoesArchimedeaFixed") {
for (const result of pack.components) { for (const result of pack.components) {
purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};'; purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
combineInventoryChanges(purchaseResponse.InventoryChanges, await addItem(inventory, result.Item, 1)); combineInventoryChanges(
purchaseResponse.InventoryChanges,
(await addItem(inventory, result.Item, 1)).InventoryChanges
);
} }
} else { } else {
for (let i = 0; i != quantity; ++i) { for (let i = 0; i != quantity; ++i) {
@ -429,7 +432,7 @@ const handleBoosterPackPurchase = async (
purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};'; purchaseResponse.BoosterPackItems += toStoreItem(result.Item) + ',{"lvl":0};';
combineInventoryChanges( combineInventoryChanges(
purchaseResponse.InventoryChanges, purchaseResponse.InventoryChanges,
await addItem(inventory, result.Item, 1) (await addItem(inventory, result.Item, 1)).InventoryChanges
); );
} }
} }
@ -465,7 +468,7 @@ const handleTypesPurchase = async (
logger.debug(`type category ${typeCategory}`); logger.debug(`type category ${typeCategory}`);
switch (typeCategory) { switch (typeCategory) {
default: default:
return { InventoryChanges: await addItem(inventory, typesName, quantity) }; return await addItem(inventory, typesName, quantity);
case "BoosterPacks": case "BoosterPacks":
return handleBoosterPackPurchase(typesName, inventory, quantity); return handleBoosterPackPurchase(typesName, inventory, quantity);
case "SlotItems": case "SlotItems":

View File

@ -19,6 +19,12 @@ 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;

File diff suppressed because it is too large Load Diff