import { APIRoute } from "astro"; import validateAccessToken from "../../../lib/utils/validateAccessToken"; import { getUserByAccessToken } from "../../../lib/db/users"; import { ObjectId } from "mongodb"; import locationManager from "../../../lib/classes/managers/LocationManager"; import { getAllShips } from "../../../lib/db/ships"; import DBShip from "../../../types/db/DBShip"; import { Planet } from "../../../lib/classes/managers/PlanetManager"; import MissionType from "../../../types/MissionType"; import { getAllResources } from "../../../lib/db/resources"; import FleetManager from "../../../lib/classes/managers/FleetManager"; export const POST: APIRoute = async({ request }) => { const response = await validateAccessToken(request); if(response instanceof Response) return response; const userDB = await getUserByAccessToken(response); if(userDB === null) { return new Response( JSON.stringify({ code: 401, message: "Unauthorized" }), { status: 401 } ) } const user = locationManager.getUser(userDB._id); if(!user) { return new Response( JSON.stringify({ code: 401, message: "Unauthorized" }), { status: 401 } ) } let body: { source: string, destination: string, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>, mission: MissionType }; try { body = await request.json() } catch(e) { return new Response( JSON.stringify({ code: 400, message: "Bad Request", error: "Invalid JSON body" }), { status: 400 } ) } const checkSource = checkPlanetId(body.source, 'source'); if(typeof checkSource.error !== "undefined") return new Response(JSON.stringify(checkSource), { status: checkSource.code }); const checkDestination = checkPlanetId(body.destination, 'destination'); if(typeof checkDestination.error !== "undefined") return new Response(JSON.stringify(checkDestination), { status: checkDestination.code }); const source = checkSource.planet; const destination = checkDestination.planet; const shipsDB = await getAllShips(); const checkShipsBody = checkShips(body.ships, shipsDB, source); if(typeof checkShipsBody.error !== "undefined") return new Response(JSON.stringify(checkShipsBody), { status: checkShipsBody.code }); const resourcesDB = await getAllResources(); await source.resources.calculateCurrentAvailableResources(); const checkCargoBody = checkCargo(body.cargo, body.ships, source, body.mission); if(typeof checkCargoBody.error !== "undefined") return new Response(JSON.stringify(checkCargoBody), { status: checkCargoBody.code }); const fleetManager = new FleetManager( new ObjectId(), source, destination, new Date(), new Date(Date.now() + 1000 * 30), //TODO: calculate time based on distance false, body.mission, body.ships, body.cargo ); const resourceDiff = await source.resources.getDifference(body.cargo.map(c => ({ id: c.id, amount: c.amount }))); for(const res of resourceDiff) { if(res.amount < 0) { return new Response( JSON.stringify({ code: 400, message: "Bad Request", error: "Not enough resource with ID '" + res.id + "' on source planet" }), { status: 400 } ) } } source.resources.setAmount(resourceDiff); await source.resources.sync(); for(const ship of body.ships) { source.ships.removeShips(ship.id, ship.amount); } await source.ships.sync(); await fleetManager.sync(); locationManager.addFleet(fleetManager); return new Response( JSON.stringify({ code: 200, message: "OK" }), { status: 200 } ) } function checkPlanetId(id: string, type: string) { if(typeof ObjectId === "undefined") return { code: 400, message: "Bad Request", error: `Missing '${type}' in body` } let idToCheck; try { idToCheck = new ObjectId(id); } catch(e) { return { code: 400, message: "Bad Request", error: `Invalid ID in '${type}'` } } const planet = locationManager.getPlanet(idToCheck); if(!planet) return { code: 404, message: "Not Found", error: `Non-existent planet provided in '${type}'` } return { code: 200, message: "OK", planet } } function checkShips(ships: Array<{ id: string, amount: number }>, shipsDB: Array, sourcePlanet: Planet) { if(typeof ships === "undefined") return { code: 400, message: "Bad Request", error: "Missing 'ships' in body" } for(let i = 0; i < ships.length; i++) { if(typeof ships[i].id === "undefined" || ships[i].id === "") return { code: 400, message: "Bad Request", error: "Missing ship ID at position " + i } if(!shipsDB.find(ship => ship.id === ships[i].id)) return { code: 404, message: "Not Found", error: "Non-existent ship ID '" + ships[i].id + "' at position " + i } if(typeof ships[i].amount === "undefined") return { code: 400, message: "Bad Request", error: "Missing ship amount for ID '" + ships[i].id + "' at position " + i } if(ships[i].amount % 1 !== 0 || ships[i].amount < 0) return { code: 400, message: "Bad Request", error: "Ship amount for ID '" + ships[i].id + "' is not a non-negative integer at position " + i } if(ships[i].amount > (sourcePlanet.ships.getShipById(ships[i].id)?.amount ?? 0)) return { code: 400, message: "Bad Request", error: "Not enough ships on planet with ID '" + ships[i].id + "' at position " + i } } return { code: 200, message: "OK" } } function checkCargo(cargo: Array<{ id: string, amount: number }>, ships: Array<{ id: string, amount: number }>, planet: Planet, missionType: MissionType) { if(missionType === "TRANSPORT" && cargo.length === 0) return { code: 400, message: "Bad Request", error: "Missing 'cargo' in body for requested mission 'TRANSPORT'" } for(let i = 0; i < cargo.length; i++) { if(typeof cargo[i].id === "undefined") return { code: 400, message: "Bad Request", error: "Missing resource ID at position " + i } if(!planet.resources.resourcesDB.find(resource => resource.id === cargo[i].id)) return { code: 404, message: "Not Found", error: "Non-existent resource ID '" + cargo[i].id + "' at position " + i } if(typeof cargo[i].amount === "undefined") return { code: 400, message: "Bad Request", error: "Missing resource amount for ID '" + cargo[i].id + "' at position " + i } if(cargo[i].amount % 1 !== 0 || cargo[i].amount < 0) return { code: 400, message: "Bad Request", error: "Resource amount for ID '" + cargo[i].id + "' is not a non-negative integer at position " + i } if(cargo[i].amount > (planet.resources.resources.find(res => res.id === cargo[i].id)?.amount ?? 0)) return { code: 400, message: "Bad Request", error: "Not enough resources on planet with ID '" + cargo[i].id + "' at position " + i } } const totalCargoAvailable = { solid: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.solid ?? 0) * ship.amount, 0), liquid: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.liquid ?? 0) * ship.amount, 0), gas: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.gas ?? 0) * ship.amount, 0) } const totalCargoUsed = { solid: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "solid" ? acc + resource.amount : acc, 0), liquid: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "liquid" ? acc + resource.amount : acc, 0), gas: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "gas" ? acc + resource.amount : acc, 0) } if(totalCargoUsed.solid > totalCargoAvailable.solid) return { code: 400, message: "Bad Request", error: "Not enough solid cargo capacity on ships" } if(totalCargoUsed.liquid > totalCargoAvailable.liquid) return { code: 400, message: "Bad Request", error: "Not enough liquid cargo capacity on ships" } if(totalCargoUsed.gas > totalCargoAvailable.gas) return { code: 400, message: "Bad Request", error: "Not enough gas cargo capacity on ships" } //TODO: check for fuel return { code: 200, message: "OK" } }