diff --git a/src/controllers/api/createGuildDojoController.ts b/src/controllers/api/createGuildDojoController.ts new file mode 100644 index 00000000..16172cdd --- /dev/null +++ b/src/controllers/api/createGuildDojoController.ts @@ -0,0 +1,41 @@ +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { getDojoClient, getGuildForRequest, getGuildLogForRequest } from "@/src/services/guildService"; +import { IOid } from "@/src/types/commonTypes"; +import { RequestHandler } from "express"; +import { Types } from "mongoose"; + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +export const createGuildDojoController: RequestHandler = async (req, res) => { + const guild = await getGuildForRequest(req); + const guildLog = await getGuildLogForRequest(req); + const payload = getJSONfromString(String(req.body)) as ICreateGuildDojoRequest; + // Populate dojo info if not present + var componentId = new Types.ObjectId(); + if (!guild.DojoComponents || guild.DojoComponents.length == 0) { + guild.DojoComponents?.push({ + _id: componentId, + pf: payload.SpawnComponent.pf, + ppf: "", + CompletionTime: new Date(Date.now()), + DecoCapacity: 600 + }); + await guild.save(); + guildLog.RoomChanges.push({ + dateTime: new Date(Date.now()), + entryType: 1, + details: payload.SpawnComponent.pf + }); + await guildLog.save(); + } + res.json(await getDojoClient(guild, 0, componentId)); +}; + +export interface ICreateGuildDojoRequest { + SpawnComponent: ICreateGuildDojoSpawnComponent; +} + +export interface ICreateGuildDojoSpawnComponent { + id: IOid; + pf: string; + ppf: string; +} diff --git a/src/controllers/api/getGuildLogController.ts b/src/controllers/api/getGuildLogController.ts index 2919ce31..72b67bcf 100644 --- a/src/controllers/api/getGuildLogController.ts +++ b/src/controllers/api/getGuildLogController.ts @@ -1,11 +1,15 @@ +import { getGuildForGuildId, getGuildLog } from "@/src/services/guildService"; +import { getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; import { RequestHandler } from "express"; -export const getGuildLogController: RequestHandler = (_req, res) => { - res.json({ - RoomChanges: [], - TechChanges: [], - RosterActivity: [], - StandingsUpdates: [], - ClassChanges: [] - }); +export const getGuildLogController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const guild = await getGuildForGuildId(inventory.GuildId?.toString() ?? ""); + if (!guild) { + res.status(400).json({ error: "guild was undefined" }); + return; + } + res.json(await getGuildLog(accountId, guild)); }; diff --git a/src/controllers/api/startDojoRecipeController.ts b/src/controllers/api/startDojoRecipeController.ts index ee7bb202..ffe21dc5 100644 --- a/src/controllers/api/startDojoRecipeController.ts +++ b/src/controllers/api/startDojoRecipeController.ts @@ -1,6 +1,6 @@ import { RequestHandler } from "express"; import { IDojoComponentClient } from "@/src/types/guildTypes"; -import { getDojoClient, getGuildForRequest, processDojoBuildMaterialsGathered } from "@/src/services/guildService"; +import { getDojoClient, getGuildForRequest, getGuildLogForRequest, processDojoBuildMaterialsGathered } from "@/src/services/guildService"; import { Types } from "mongoose"; import { ExportDojoRecipes } from "warframe-public-export-plus"; import { config } from "@/src/services/configService"; @@ -12,6 +12,7 @@ interface IStartDojoRecipeRequest { export const startDojoRecipeController: RequestHandler = async (req, res) => { const guild = await getGuildForRequest(req); + const guildLog = await getGuildLogForRequest(req); // At this point, we know that a member of the guild is making this request. Assuming they are allowed to start a build. const request = JSON.parse(String(req.body)) as IStartDojoRecipeRequest; @@ -33,6 +34,12 @@ export const startDojoRecipeController: RequestHandler = async (req, res) => { DecoCapacity: room?.decoCapacity }) - 1 ]; + guildLog.RoomChanges.push({ + dateTime: new Date(Date.now()), + entryType: 2, + details: request.PlacedComponent.pf + }); + await guildLog.save(); if (config.noDojoRoomBuildStage) { component.CompletionTime = new Date(Date.now()); if (room) { diff --git a/src/models/guildModel.ts b/src/models/guildModel.ts index 9815d257..abb7da88 100644 --- a/src/models/guildModel.ts +++ b/src/models/guildModel.ts @@ -5,6 +5,9 @@ import { ITechProjectClient, IDojoDecoDatabase, ILongMOTD, + IGuildLogDatabase, + IGuildLogItemDatabase, + IGuildLogItemClient, IGuildMemberDatabase } from "@/src/types/guildTypes"; import { Document, Model, model, Schema, Types } from "mongoose"; @@ -125,3 +128,62 @@ const guildMemberSchema = new Schema({ guildMemberSchema.index({ accountId: 1, guildId: 1 }, { unique: true }); export const GuildMember = model("GuildMember", guildMemberSchema); + +const guildLogItemSchema = new Schema( + { + dateTime: Date, + entryType: Number, + details: String + }, + { _id: false } +); + +guildLogItemSchema.set("toJSON", { + virtuals: true, + transform(_doc, obj) { + const db = obj as IGuildLogItemDatabase; + const client = obj as IGuildLogItemClient; + if (db.dateTime) { + client.dateTime = toMongoDate(db.dateTime); + } + } +}); + +const guildLogSchema = new Schema( + { + accountId: Types.ObjectId, + guildId: Types.ObjectId, + RoomChanges: [guildLogItemSchema], + TechChanges: [guildLogItemSchema], + RosterActivity: [guildLogItemSchema], + StandingsUpdates: [guildLogItemSchema], + ClassChanges: [guildLogItemSchema] + }, + { id: false } +); + +guildLogSchema.index({ accountId: 1, guildId: 1 }, { unique: true }); + +type GuildLogDocumentProps = { + RoomChanges: Types.DocumentArray; + TechChanges: Types.DocumentArray; + RosterActivity: Types.DocumentArray; + StandingsUpdates: Types.DocumentArray; + ClassChanges: Types.DocumentArray; +}; + +// eslint-disable-next-line @typescript-eslint/ban-types +type GuildLogModel = Model; + +export const GuildLog = model("GuildLog", guildLogSchema); + +export type TGuildLogDatabaseDocument = Document & + Omit< + IGuildLogDatabase & { + _id: Types.ObjectId; + } & { + __v: number; + }, + keyof GuildLogDocumentProps + > & + GuildLogDocumentProps; \ No newline at end of file diff --git a/src/routes/api.ts b/src/routes/api.ts index 75ddf345..968ef81b 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -20,6 +20,7 @@ import { contributeGuildClassController } from "@/src/controllers/api/contribute import { contributeToDojoComponentController } from "@/src/controllers/api/contributeToDojoComponentController"; import { contributeToVaultController } from "@/src/controllers/api/contributeToVaultController"; import { createGuildController } from "@/src/controllers/api/createGuildController"; +import { createGuildDojoController } from "@/src/controllers/api/createGuildDojoController"; import { creditsController } from "@/src/controllers/api/creditsController"; import { declineGuildInviteController } from "@/src/controllers/api/declineGuildInviteController"; import { deleteSessionController } from "@/src/controllers/api/deleteSessionController"; @@ -169,6 +170,7 @@ apiRouter.post("/contributeGuildClass.php", contributeGuildClassController); apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentController); apiRouter.post("/contributeToVault.php", contributeToVaultController); apiRouter.post("/createGuild.php", createGuildController); +apiRouter.post("/createGuildDojo.php", createGuildDojoController); apiRouter.post("/destroyDojoDeco.php", destroyDojoDecoController); apiRouter.post("/dojoComponentRush.php", dojoComponentRushController); apiRouter.post("/drones.php", dronesController); diff --git a/src/services/guildService.ts b/src/services/guildService.ts index 3d53a211..56c46938 100644 --- a/src/services/guildService.ts +++ b/src/services/guildService.ts @@ -1,7 +1,13 @@ import { Request } from "express"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { addRecipes, getInventory } from "@/src/services/inventoryService"; -import { Guild, GuildMember, TGuildDatabaseDocument } from "@/src/models/guildModel"; +import { + Guild, + GuildLog, + GuildMember, + TGuildDatabaseDocument, + TGuildLogDatabaseDocument +} from "@/src/models/guildModel"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { IDojoClient, @@ -329,3 +335,37 @@ export const createUniqueClanName = async (name: string): Promise => { } while (discriminator != initialDiscriminator); throw new Error(`clan name is so unoriginal it's already been done 1000 times: ${name}`); }; + +export const getGuildLogForRequest = async (req: Request): Promise => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const guildId = inventory.GuildId!.toString(); + const guildLog = await GuildLog.findOne({ _id: guildId }); + if (!guildLog) { + throw new Error("Account thinks it is in a guild that doesn't exist"); + } + return guildLog; +}; + +export const getGuildLog = async (accountId: string, guild: TGuildDatabaseDocument) => { + const guildLog = await GuildLog.findOne({ guildId: guild._id }); + if (!guildLog) { + return await GuildLog.insertOne({ + _id: new Types.ObjectId(), + accountId: accountId, + guildId: guild._id, + RoomChanges: [], + TechChanges: [], + RosterActivity: [], + StandingsUpdates: [], + ClassChanges: [] + }); + } + return { + RoomChanges: guildLog!.RoomChanges, + TechChanges: guildLog!.TechChanges, + RosterActivity: guildLog!.RosterActivity, + StandingsUpdates: guildLog!.StandingsUpdates, + ClassChanges: guildLog!.ClassChanges + }; +}; diff --git a/src/types/guildTypes.ts b/src/types/guildTypes.ts index 7f241212..f9d976df 100644 --- a/src/types/guildTypes.ts +++ b/src/types/guildTypes.ts @@ -160,3 +160,25 @@ export interface ITechProjectClient { export interface ITechProjectDatabase extends Omit { CompletionDate?: Date; } + +export interface IGuildLogItemClient { + dateTime: IMongoDate; + entryType: number; + details: string; +} + +export interface IGuildLogDatabase { + accountId: Types.ObjectId; + guildId: Types.ObjectId; + RoomChanges: IGuildLogItemDatabase[]; + TechChanges: IGuildLogItemDatabase[]; + RosterActivity: IGuildLogItemDatabase[]; + StandingsUpdates: IGuildLogItemDatabase[]; + ClassChanges: IGuildLogItemDatabase[]; +} + +export interface IGuildLogItemDatabase extends Omit { + dateTime: Date; + entryType: number; + details: string; +}