Compare commits

..

No commits in common. "cc5d482a0f3b47e9a0f7e433952c33d6a437a91f" and "b13a116cd4d6ac98edce9389144ba439f53d24ef" have entirely different histories.

59 changed files with 728 additions and 1043 deletions

View File

@ -3,7 +3,6 @@ import { getHighestWeightedLanguage, getLocales, getName } from '../lib/utils/la
interface Props { interface Props {
id: string; id: string;
level: string;
name: string; name: string;
description: string; description: string;
image: string; image: string;
@ -18,7 +17,7 @@ const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.hea
<div class="item-card" data-id={Astro.props.id}> <div class="item-card" data-id={Astro.props.id}>
<img class="item-card-image" src={Astro.props.image} /> <img class="item-card-image" src={Astro.props.image} />
<div class="item-card-main-field"> <div class="item-card-main-field">
<div class="item-card-name">{Astro.props.name} | {Astro.props.level}</div> <div class="item-card-name">{Astro.props.name}</div>
<div class="item-card-description">{Astro.props.description}</div> <div class="item-card-description">{Astro.props.description}</div>
<a id={`button_${Astro.props.id}`} href="#" class="item-card-build">{getName(lang, Astro.props.button_type, Astro.props.button_name)}</a> <a id={`button_${Astro.props.id}`} href="#" class="item-card-build">{getName(lang, Astro.props.button_type, Astro.props.button_name)}</a>
<div class="item-card-info-button">i</div> <div class="item-card-info-button">i</div>

View File

@ -1,26 +1,20 @@
--- ---
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { calculateCurrentAvailableResources } from '../lib/utils/resourceManager';
import { getHighestWeightedLanguage, getLocales, getName } from '../lib/utils/langDriver'; import { getHighestWeightedLanguage, getLocales, getName } from '../lib/utils/langDriver';
import { getAllResources } from '../lib/db/resources'; import { getAllResources } from '../lib/db/resources';
import locationManager from '../lib/classes/managers/LocationManager';
import { Resource } from '../lib/classes/managers/ResourceManager';
const resourceTypes = await getAllResources(); const resourceTypes = await getAllResources();
const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.headers.get('accept-language'))); const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.headers.get('accept-language')));
const planetId = new ObjectId(Astro.cookies.get('planetid')?.value ?? ''); const resources = await calculateCurrentAvailableResources(new ObjectId(Astro.cookies.get('planetid')?.value ?? ''));
const resources = locationManager.getPlanet(planetId)?.resources; const resourceArray = [];
for(const key in resources) {
if(!resources) return; resourceArray.push(resources[key as never]);
resources.calculateCurrentAvailableResources();
const resourceArray: Resource[] = [];
for(const key of resources.resources) {
resourceArray.push(key);
} }
--- ---
<div id="resourcebar"> <div id="resourcebar">
<div class="resourcebar-item-identifier"> <div class="resourcebar-item-identifier">
@ -29,16 +23,16 @@ for(const key of resources.resources) {
<div id="resourcebar-elements" class="resourcebar-elements"> <div id="resourcebar-elements" class="resourcebar-elements">
{resourceArray.map(res => {resourceArray.map(res =>
<div class="resourcebar-item" <div class="resourcebar-item"
data-res-type={resourceTypes.find(x => x.id === res.id)?.type ?? "solid"} data-res-type={resourceTypes.find(x => x.id === res.name)?.type ?? "solid"}
data-res-amount={res.amount} data-res-amount={res.amount}
data-res-mining-rate={res.perHourMiningRate} data-res-mining-rate={res.perHourMiningRate}
style={(resourceTypes.find(x => x.id === res.id)?.type ?? "solid") === "solid" ? "" : "display: none;"} style={(resourceTypes.find(x => x.id === res.name)?.type ?? "solid") === "solid" ? "" : "display: none;"}
> >
<div class="resourcebar-item-icon"> <div class="resourcebar-item-icon">
<img src={resourceTypes.find(x => x.id === res.id)?.icon ?? "#"} alt={res.id} /> <img src={resourceTypes.find(x => x.id === res.name)?.icon ?? "#"} alt={res.name} />
</div> </div>
<div class="resourcebar-item-text-wrapper" data-resname={res.id}> <div class="resourcebar-item-text-wrapper" data-resname={res.name}>
<div class="resourcebar-item-text">{getName(lang, 'resources', res.id)}</div> <div class="resourcebar-item-text">{getName(lang, 'resources', res.name)}</div>
<div class="resourcebar-item-amount">[fetching]</div> <div class="resourcebar-item-amount">[fetching]</div>
</div> </div>
<div class="resourcebar-item-tooltip"> <div class="resourcebar-item-tooltip">

View File

@ -1,70 +0,0 @@
import DBBuilding from "../../types/db/DBBuilding";
import BuildingManager from "./managers/BuildingManager";
export default class Building {
manager: BuildingManager
data: DBBuilding;
level: number;
constructor(manager: BuildingManager, data: DBBuilding, level: number) {
this.manager = manager;
this.data = data;
this.level = level;
}
async checkRequiredResources(level: number): Promise<boolean> {
const resources = await this.manager.planet.resources.calculateCurrentAvailableResources();
const requirements = this.data.requirements.resources;
let canBuild = true;
for(let res of requirements) {
const resource = resources.find(r => r.id === res.id);
if(!resource) return false;
const required = Math.pow(this.data.multiplier, level) * res.amount;
if(resource.amount < required) {
canBuild = false;
break;
}
}
return canBuild;
}
async checkRequirements(): Promise<{ canBuild: boolean, error: string }> {
const playerBuildings = this.manager.buildings;
let playerBuildingsCanBuild = { canBuild: true, missing: "" };
this.data.requirements.buildings.forEach((buildingReq: any) => {
if(playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0 < buildingReq.level) {
playerBuildingsCanBuild = { canBuild: false, missing: `${buildingReq.id} level ${buildingReq.level} required, found ${playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0}` };
return;
}
});
if(!playerBuildingsCanBuild.canBuild) return {
canBuild: false,
error: playerBuildingsCanBuild.missing
}
// research
const playerResearch = this.manager.planet.manager.owner.research;
let playerResearchCanBuild = { canBuild: true, missing: "" };
for(const researchReq of this.data.requirements.research) {
if(playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0 < researchReq.level) {
playerResearchCanBuild = { canBuild: false, missing: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0}` };
}
};
if(!playerResearchCanBuild.canBuild) return {
canBuild: false,
error: playerResearchCanBuild.missing
}
return {
canBuild: true,
error: ""
}
}
}

View File

@ -1,32 +0,0 @@
import { ObjectId } from "mongodb";
import ResearchManager from "./managers/ResearchManager";
import { Planet } from "./managers/PlanetManager";
export default class User {
id: ObjectId;
username: string;
email: string;
createdAt: Date;
updatedAt: Date;
lastLogin: Date;
research: ResearchManager;
mainPlanet!: Planet;
constructor(id: ObjectId, username: string, email: string, createdAt: Date, updatedAt: Date, lastLogin: Date) {
this.id = id;
this.username = username;
this.email = email;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
this.lastLogin = lastLogin;
this.research = new ResearchManager(this);
}
async init() {
await this.research.init();
}
addMainPlanet(planet: Planet) {
this.mainPlanet = planet;
}
}

View File

@ -1,48 +0,0 @@
import { getAllBuildings } from '../../db/buildings';
import DBBuilding from '../../../types/db/DBBuilding';
import { Planet } from './PlanetManager';
import { updatePlanetBuildings } from '../../db/planets';
import Building from '../Building';
export default class BuildingManager {
buildings: Array<Building> = [];
buildingsDB: Array<DBBuilding> = []
planet: Planet;
constructor(planet: Planet) {
this.planet = planet;
}
async init(buildingData: { id: string, level: number }[]) {
this.buildingsDB = await getAllBuildings();
buildingData.forEach(building => {
const buildingToFind = this.buildingsDB.find(b => b.id === building.id);
if(buildingToFind) this.buildings.push(new Building(
this,
buildingToFind,
building.level
))
})
return this;
}
getBuildingById(id: string) {
return this.buildings.find(building => building.data.id === id);
}
addBuilding(building: Building) {
const findBuilding = this.buildings.find(b => b.data.id === building.data.id);
if(!findBuilding) this.buildings.push(building);
else findBuilding.level++;
}
removeBuilding(id: string) {
this.buildings = this.buildings.filter(building => building.data.id !== id);
}
async sync() {
await updatePlanetBuildings(this.planet._id, this.buildings.map(building => { return { id: building.data.id, level: building.level } }));
}
}

View File

@ -1,153 +0,0 @@
import { ObjectId } from "mongodb";
import PlanetManager, { Planet } from "./PlanetManager";
import { getAllGalaxies } from "../../db/galaxies";
import { getSectorById } from "../../db/sectors";
import { getSystemById } from "../../db/systems";
import { getAllUsers } from "../../db/users";
import User from "../User";
export type Galaxy = {
_id: ObjectId,
name: string,
sectors: Array<Sector>
}
export type Sector = {
_id: ObjectId,
galaxy: Galaxy,
name: string,
expedition: null,
systems: Array<System>
}
export type System = {
_id: ObjectId,
sector: Sector,
name: string,
ownedBy: User,
planets: PlanetManager
}
class LocationManager {
private static instance: LocationManager;
private constructor() {}
public static getInstance(): LocationManager {
if (!LocationManager.instance) {
LocationManager.instance = new LocationManager();
}
return LocationManager.instance;
}
galaxies: Galaxy[] = [];
users: User[] = [];
async init() {
const users = await getAllUsers();
users.forEach(async user => {
this.users.push(new User(user._id, user.username, user.email, user.createdAt, user.updatedAt, user.lastLogin));
});
const galaxies = await getAllGalaxies();
for(const galaxy of galaxies) {
const galaxyObject: Galaxy = {
_id: galaxy._id,
name: galaxy.name,
sectors: []
};
for(const sectorId of galaxy.sectors) {
const sectorData = await getSectorById(sectorId);
const sectorObject: Sector = {
_id: sectorData._id,
galaxy: galaxyObject,
name: sectorData.name,
expedition: null,
systems: []
};
for(const systemId of sectorData.systems) {
const systemData = await getSystemById(systemId);
const user = this.users.find(user => user.id.equals(systemData.ownedBy));
if(!user) throw new Error("User not found");
const systemObject: System = {
_id: systemData._id,
sector: sectorObject,
name: systemData.name,
ownedBy: user,
planets: new PlanetManager(user)
};
await systemObject.planets.fillData(systemData.planets);
sectorObject.systems.push(systemObject);
};
galaxyObject.sectors.push(sectorObject);
};
this.galaxies.push(galaxyObject);
};
for(const user of this.users) {
await user.init();
const userDB = users.find(u => u._id.equals(user.id));
const mainPlanet = this.getPlanet(userDB?.mainPlanet as ObjectId);
if(!mainPlanet) throw new Error("Main planet not found");
user.mainPlanet = mainPlanet;
};
return this;
}
getGalaxy(_id: ObjectId) {
return this.galaxies.find(galaxy => galaxy._id.equals(_id));
}
getSector(_id: ObjectId) {
let foundSector: Sector | undefined;
this.galaxies.find(galaxy => galaxy.sectors.find(sector => {
if(sector._id.equals(_id)) foundSector = sector;
}));
return foundSector;
}
getSystem(_id: ObjectId) {
let foundSystem: System | undefined;
this.galaxies.find(galaxy => galaxy.sectors.find(sector => sector.systems.find(system => {
if(system._id.equals(_id)) foundSystem = system
})));
return foundSystem
}
getPlanet(_id: ObjectId) {
let foundPlanet: Planet | undefined;
for(const galaxy of this.galaxies) {
for(const sector of galaxy.sectors) {
for(const system of sector.systems) {
foundPlanet = system.planets.getPlanetById(_id);
if(foundPlanet) break;
}
if(foundPlanet) break;
}
if(foundPlanet) break;
}
return foundPlanet;
}
getUser(_id: ObjectId) {
return this.users.find(user => user.id.equals(_id));
}
}
const locationManager = LocationManager.getInstance();
locationManager.init();
export default locationManager;

View File

@ -1,51 +0,0 @@
import { ObjectId } from "mongodb";
import BuildingManager from "./BuildingManager";
import { getPlanetById } from "../../db/planets";
import ResourceManager from "./ResourceManager";
import User from "../User";
export type Planet = {
_id: ObjectId;
manager: PlanetManager;
name: string;
fields: number;
resources: ResourceManager;
buildings: BuildingManager;
ships: Array<any>;
}
export default class PlanetManager {
planets: Array<Planet> = [];
owner: User;
constructor(user: User) {
this.owner = user;
}
async fillData(planets: ObjectId[]) {
await Promise.all(planets.map(async planetId => {
const planet = await getPlanetById(planetId);
const planetObject: Planet = {
_id: planet._id,
manager: this,
name: planet.name,
fields: planet.fields,
//@ts-ignore
resources: null,
//@ts-ignore
buildings: null,
ships: planet.ships
}
planetObject.resources = await new ResourceManager(planetObject).init(planet.resources);
planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings);
this.planets.push(planetObject);
}));
}
getPlanetById(id: ObjectId) {
return this.planets.find(planet => planet._id.equals(id));
}
}

View File

@ -1,50 +0,0 @@
import DBResearch from "../../../types/db/DBResearch";
import { getAllResearch } from "../../db/research";
import { getUserResearch, updateUserResearch } from "../../db/users";
import User from "../User";
export type Research = {
id: string,
level: number,
data: DBResearch
}
export default class ResearchManager {
research: Array<Research> = [];
researchDB: Array<DBResearch> = [];
owner: User;
constructor(user: User) {
this.owner = user;
}
async init() {
this.researchDB = await getAllResearch();
const research = await getUserResearch(this.owner);
this.research = research.map(r => {
const researchData = this.researchDB.find(research => research.id === r.id);
if(!researchData) throw new Error(`Research ${r.id} not found`);
return {
id: r.id,
level: r.level,
data: researchData
}
});
return this;
}
getResearchById(id: string) {
return this.research.find(research => research.data.id === id);
}
addResearch(research: Research) {
const findResearch = this.research.find(r => r.data.id === research.data.id);
if(!findResearch) this.research.push(research);
else findResearch.level++;
}
async sync() {
await updateUserResearch(this.owner, this.research.map(r => { return { id: r.data.id, level: r.level } }));
}
}

View File

@ -1,152 +0,0 @@
import DBResource from "../../../types/db/DBResource";
import { updatePlanetResources } from "../../db/planets";
import { getAllResources } from "../../db/resources";
import { Planet } from "./PlanetManager";
export type Resource = {
id: string,
amount: number,
lastUpdated: Date,
perHourMiningRate: number,
data: DBResource
}
export default class ResourceManager {
resources: Array<Resource> = [];
planet: Planet;
constructor(planet: Planet) {
this.planet = planet;
}
async init(resourceData: { id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }[]) {
const resources = await getAllResources();
if(resourceData.length === 0) {
resourceData = [
{
id: "coal",
amount: 11,
lastUpdated: new Date(),
perHourMiningRate: 11
},
{
id: "iron",
amount: 22,
lastUpdated: new Date(),
perHourMiningRate: 22
},
{
id: "gold",
amount: 33,
lastUpdated: new Date(),
perHourMiningRate: 33
},
{
id: "water",
amount: 44,
lastUpdated: new Date(),
perHourMiningRate: 44
},
{
id: "sulfuric-acid",
amount: 55,
lastUpdated: new Date(),
perHourMiningRate: 55
},
{
id: "liquid-nitrogen",
amount: 66,
lastUpdated: new Date(),
perHourMiningRate: 66
},
{
id: "hydrogen",
amount: 77,
lastUpdated: new Date(),
perHourMiningRate: 77
},
{
id: "oxygen",
amount: 88,
lastUpdated: new Date(),
perHourMiningRate: 88
},
{
id: "helium-3",
amount: 99,
lastUpdated: new Date(),
perHourMiningRate: 99
}
];
await updatePlanetResources(this.planet._id, this.resources);
}
resourceData.forEach(resource => {
const resFromDB = resources.find(res => res.id === resource.id);
if(resFromDB) this.resources.push({
id: resource.id,
amount: resource.amount,
lastUpdated: resource.lastUpdated,
perHourMiningRate: resource.perHourMiningRate,
data: resFromDB
})
});
return this;
}
getResourceById(resId: string) {
return this.resources.find(res => res.id === resId);
}
async calculateCurrentAvailableResources() {
this.resources.forEach(res => {
const timeDiff = Math.abs((new Date()).getTime() - res.lastUpdated.getTime());
const hours = timeDiff / (1000 * 60 * 60);
const amountToAdd = hours * res.perHourMiningRate;
res.amount += amountToAdd;
res.lastUpdated = new Date();
});
await updatePlanetResources(this.planet._id, this.resources);
return this.resources;
}
async getDifference(resources: { id: string, amount: number }[]): Promise<Array<Resource>> {
const currentResources = await this.calculateCurrentAvailableResources();
const difference: Resource[] = [];
currentResources.forEach(res => {
const currentRes = resources.find(r => r.id === res.id);
if(currentRes) difference.push({
id: res.id,
amount: res.amount - currentRes.amount,
lastUpdated: res.lastUpdated,
perHourMiningRate: res.perHourMiningRate,
data: res.data
});
else difference.push(res);
});
return difference;
}
update(resources: Resource[]) {
this.resources = resources;
}
async sync() {
await updatePlanetResources(this.planet._id, this.resources.map(res => {
return {
id: res.id,
amount: res.amount,
lastUpdated: res.lastUpdated,
perHourMiningRate: res.perHourMiningRate
}
}));
}
}

View File

@ -1,9 +1,7 @@
import { AccessTokens } from './mongodb'; import { AccessTokens } from './mongodb';
import type AccessToken from '../../types/AccessToken';
import { createHash } from 'crypto'; import { createHash } from 'crypto';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import DBAccessToken from '../../types/db/DBAccessToken';
import AccessToken from '../../types/AccessToken';
import locationManager from '../classes/managers/LocationManager';
export const createAccessToken = async (accessToken: AccessToken) => { export const createAccessToken = async (accessToken: AccessToken) => {
const newAccessToken = await (await AccessTokens()).insertOne(accessToken); const newAccessToken = await (await AccessTokens()).insertOne(accessToken);
@ -25,7 +23,7 @@ export const getAccessToken = async (accessToken: string) => {
{ createdAt }, { createdAt },
{ user }, { user },
{ entropy } { entropy }
] }) as Promise<DBAccessToken | null>; ] }) as Promise<AccessToken | null>;
} }
export const getAllAccessTokens = async () => { export const getAllAccessTokens = async () => {
@ -39,24 +37,7 @@ export const getAllAccessTokens = async () => {
} }
const accessTokens = await AccessTokens(); const accessTokens = await AccessTokens();
const arrayOfTokens = await accessTokens.find({}).toArray() as DBAccessToken[]; const arrayOfTokens = await accessTokens.find({}).toArray() as AccessToken[];
let arr = [master].concat(arrayOfTokens);
const arr = [master];
for(const token of arrayOfTokens) {
const user = locationManager.getUser(token.user) ?? null;
const tokenObject: AccessToken = {
type: token.type,
user,
entropy: token.entropy,
createdAt: token.createdAt,
expiresAt: token.expiresAt,
createdFrom: token.createdFrom
}
arr.push(tokenObject);
}
return arr; return arr;
} }

View File

@ -1,4 +1,4 @@
import DBBuilding from '../../types/db/DBBuilding'; import DBBuilding from '../../types/DBBuilding';
import { Buildings } from '../db/mongodb'; import { Buildings } from '../db/mongodb';
export const getAllBuildings = async () => { export const getAllBuildings = async () => {

View File

@ -1,13 +0,0 @@
import { ObjectId } from "mongodb";
import { Galaxies } from "./mongodb"
import DBGalaxy from "../../types/db/DBGalaxy";
export const getAllGalaxies = async () => {
return await (await Galaxies()).find({}).toArray() as DBGalaxy[];
}
export const getGalaxyById = async (id: ObjectId) => {
return await (await Galaxies()).findOne({
_id: id
}) as DBGalaxy;
}

View File

@ -8,6 +8,7 @@ const mongo = new MongoClient(uri, options);
export const connect = async () => { export const connect = async () => {
await mongo.connect(); await mongo.connect();
// return mongo.db(dbName);
} }
export const disconnect = async () => { export const disconnect = async () => {
@ -29,21 +30,6 @@ export const AccessTokens = async () => {
return db.collection('accessTokens'); return db.collection('accessTokens');
} }
export const Galaxies = async () => {
const db = await getDB();
return db.collection('galaxies');
}
export const Sectors = async () => {
const db = await getDB();
return db.collection('sectors');
}
export const Systems = async () => {
const db = await getDB();
return db.collection('systems');
}
export const Planets = async () => { export const Planets = async () => {
const db = await getDB(); const db = await getDB();
return db.collection('planets'); return db.collection('planets');

View File

@ -1,29 +1,74 @@
import { Planets } from '../db/mongodb'; import { Planets } from '../db/mongodb';
import type User from '../../types/User';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import DBPlanet from '../../types/db/DBPlanet'; import type Planet from '../../types/Planet';
import { getUserById } from './users';
import type Building from '../../types/Building';
import type Ship from '../../types/Ship';
import type PlayerResource from '../../types/PlayerResource';
export const getAllPlanets = async () => { export const getAllPlanets = async (options?: { fetchUserPlanets: boolean }) => {
return await (await Planets()).find({}).toArray() as DBPlanet[]; const planets = await Planets();
const fetched = (await planets.find({}).toArray()).map(async planet => {
planet.owner = await getUserById(planet.owner);
return planet;
})
return Promise.all(fetched) as unknown as Array<Planet>;
}
export const createPlanet = async (options: { name: string, owner?: User, ownerId?: ObjectId, fields?: number }) => {
if(options.owner !== undefined && options.ownerId !== undefined) throw new Error("Duplicate identifier");
let user: ObjectId;
if(options.ownerId) user = options.ownerId;
else if(options.owner) user = options.owner._id;
else throw new Error("Unknown error");
if(options.fields === undefined) options.fields = 20;
//@ts-ignore TODO: find another way of handling IDs from types before inserting to database
const planet = {
owner: user,
name: options.name,
fields: options.fields,
resources: new Array<PlayerResource>,
buildings: new Array<Building>,
ships: new Array<Ship>
}
await (await Planets()).insertOne(planet);
} }
export const getPlanetById = async (id: ObjectId) => { export const getPlanetById = async (id: ObjectId) => {
return await (await Planets()).findOne({ _id: id }) as DBPlanet; const planets = await Planets();
const planet = await planets.findOne({
_id: id
});
if(!planet) return null;
planet.owner = await getUserById(planet.owner);
return planet as Planet;
} }
export const updatePlanetResources = async (planetId: ObjectId, resources: Array<any>) => { export const createOrUpgradeBuilding = async (planetId: ObjectId, building: Building) => {
const planets = await Planets(); const planet = await getPlanetById(planetId);
await planets.updateOne({ _id: planetId }, { if(!planet) throw new Error("Planet not found");
$set: {
resources const buildingIndex = planet.buildings.findIndex(b => b.id === building.id);
} if(buildingIndex === -1) {
}); planet.buildings.push(building);
} } else {
planet.buildings[buildingIndex].level++;
}
export const updatePlanetBuildings = async (planetId: ObjectId, buildings: Array<{ id: string, level: number }>) => {
const planets = await Planets(); const planets = await Planets();
await planets.updateOne({ _id: planetId }, { await planets.updateOne({
_id: planetId
}, {
$set: { $set: {
buildings buildings: planet.buildings
} }
}); })
} }

View File

@ -1,4 +1,4 @@
import DBResearch from '../../types/db/DBResearch'; import DBResearch from '../../types/DBResearch';
import { Research } from '../db/mongodb'; import { Research } from '../db/mongodb';
export const getAllResearch = async () => { export const getAllResearch = async () => {

View File

@ -1,4 +1,4 @@
import DBResource from '../../types/db/DBResource'; import DBResource from '../../types/DBResource';
import { Resources } from '../db/mongodb'; import { Resources } from '../db/mongodb';
export const getAllResources = async () => { export const getAllResources = async () => {

View File

@ -1,13 +0,0 @@
import { ObjectId } from "mongodb";
import { Sectors } from "./mongodb";
import DBSector from "../../types/db/DBSector";
export const getAllSectors = async () => {
return await (await Sectors()).find({}).toArray() as DBSector[];
}
export const getSectorById = async (id: ObjectId) => {
return await (await Sectors()).findOne({
_id: id
}) as DBSector;
}

View File

@ -1,4 +1,4 @@
import DBShip from '../../types/db/DBShip'; import DBShip from '../../types/DBShip';
import { Ships } from '../db/mongodb'; import { Ships } from '../db/mongodb';
export const getAllShips = async () => { export const getAllShips = async () => {

View File

@ -1,13 +0,0 @@
import { ObjectId } from "mongodb";
import { Systems } from "./mongodb";
import DBSystem from "../../types/db/DBSystem";
export const getAllSystems = async () => {
return await (await Systems()).find({}).toArray() as DBSystem[];
}
export const getSystemById = async (id: ObjectId) => {
return await (await Systems()).findOne({
_id: id
}) as DBSystem;
}

View File

@ -1,30 +1,33 @@
import { Users } from '../db/mongodb'; import { Planets, Users } from '../db/mongodb';
import type User from '../../types/User';
import type AccessToken from '../../types/AccessToken';
import { ObjectId } from 'mongodb'; import { ObjectId } from 'mongodb';
import { hash } from 'argon2' import { hash } from 'argon2'
import DBUser from '../../types/db/DBUser'; import { createInitialResources } from '../utils/resourceManager';
import User from '../classes/User'; import type Research from '../../types/Research';
import AccessToken from '../../types/AccessToken';
export const getAllUsers = async () => { export const getAllUsers = async () => {
const users = await Users(); const users = await Users();
return users.find({}).toArray() as Promise<DBUser[]>; return users.find({}).toArray() as Promise<User[]>;
} }
export const createUser = async (username: string, email: string, password: string) => { export const createUser = async (username: string, email: string, password: string) => {
const user = { const user: User = {
username, username,
email, email,
password: await hash(password), password: await hash(password),
lastLogin: new Date(), lastLogin: new Date(),
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date(), updatedAt: new Date(),
research: [], //@ts-ignore
resources: {}
} }
await (await Users()).insertOne(user); await (await Users()).insertOne(user);
const newUser = await getUserByNickOrEmail(username); const newUser = await getUserByNickOrEmail(username);
if(!newUser) return user; if(!newUser) return user;
createInitialResources(newUser._id);
return newUser; return newUser;
} }
@ -37,7 +40,7 @@ export const getUserById = async (id: ObjectId) => {
const users = await Users(); const users = await Users();
return users.findOne({ return users.findOne({
_id: id _id: id
}) as Promise<DBUser | null>; }) as Promise<User | null>;
} }
export const getUserByNickOrEmail = async (searchString: string) => { export const getUserByNickOrEmail = async (searchString: string) => {
@ -47,18 +50,14 @@ export const getUserByNickOrEmail = async (searchString: string) => {
{ username: searchString }, { username: searchString },
{ email: searchString } { email: searchString }
] ]
}) as Promise<DBUser | null>; }) as Promise<User | null>;
} }
export const getUserByAccessToken = async(accessToken: string | AccessToken): Promise<DBUser | null> => { export const getUserByAccessToken = async(accessToken: string | AccessToken): Promise<User | null> => {
if(typeof accessToken === "string") { if(typeof accessToken === "string") {
const userId = new ObjectId(Buffer.from(accessToken.split(".")[2], 'base64url').toString()); const userId = new ObjectId(Buffer.from(accessToken.split(".")[2], 'base64url').toString());
return getUserById(userId); return getUserById(userId);
} else { } else return getUserById(accessToken.user as ObjectId)
const user = accessToken.user;
if(!user) return null;
return getUserById(user?.id);
}
} }
export const updateLastLogin = async (user: User) => { export const updateLastLogin = async (user: User) => {
@ -66,14 +65,54 @@ export const updateLastLogin = async (user: User) => {
return users.updateOne({ username: user.username }, { $set: { lastLogin: new Date() } }); return users.updateOne({ username: user.username }, { $set: { lastLogin: new Date() } });
} }
export const getUserResearch = async (user: User): Promise<Array<{ id: string, level: number }>> => { export const getUserResearch = async (user: User): Promise<Array<Research>> => {
const users = await Users(); const research: Array<Research> = [];
const rawUser = await users.findOne({ username: user.username });
if (!rawUser) return []; if (user?.research !== undefined) {
return rawUser.research; user?.research.forEach((res: Research) => {
research.push(res);
});
}
return research;
} }
export const updateUserResearch = async (user: User, research: Array<{ id: string, level: number }>) => { export const createOrUpgradeResearch = async (userId: ObjectId, research: Research) => {
const users = await Users(); const users = await Users();
users.updateOne({ username: user.username }, { $set: { research } });
const result = await users.updateOne(
{ _id: userId, "research.id": research.id },
{ $set: { "research.$.level": research.level } }
);
if (result.modifiedCount === 0) {
await users.updateOne({ _id: userId },
{ $push: { research } }
);
}
}
export const getUserWithPlanets = async (id: ObjectId): Promise<User | null> => {
const users = await Users();
const planets = await Planets();
const rawUser = await users.findOne({
_id: id
})
if (!rawUser) return null;
const userPlanets = (await planets.find({
owner: id
}).toArray()).map(planet => {
planet.owner = rawUser;
return planet;
});
rawUser.planets = {
partial: false,
data: userPlanets
}
return rawUser as User;
} }

View File

@ -0,0 +1,23 @@
import type Resource from "../../types/Resource";
import type Resources from "../../types/Resources";
export default function calculateAvailableResources(userResources: Array<Resource>, buildingCost: Array<Resource>) {
let canBuild = true;
const resources = {} as Array<Resource>;
userResources.forEach((userResource) => {
const resource = buildingCost.find((buildingResource) => buildingResource.name === userResource.name);
if (resource) {
resources.push({
name: userResource.name,
amount: userResource.amount - resource.amount
});
if (userResource.amount < resource.amount) canBuild = false;
}
});
return {
canBuild,
resources
};
}

View File

@ -0,0 +1,100 @@
import { ObjectId } from "mongodb"
import { Planets } from "../db/mongodb";
import type PlayerResource from "../../types/PlayerResource";
import { getPlanetById } from "../db/planets";
export const createInitialResources = async (planetId: ObjectId) => {
const resources: Array<PlayerResource> = [
{
name: "coal",
amount: 11,
lastUpdated: new Date(),
perHourMiningRate: 11
},
{
name: "iron",
amount: 22,
lastUpdated: new Date(),
perHourMiningRate: 22
},
{
name: "gold",
amount: 33,
lastUpdated: new Date(),
perHourMiningRate: 33
},
{
name: "water",
amount: 44,
lastUpdated: new Date(),
perHourMiningRate: 44
},
{
name: "sulfuricAcid",
amount: 55,
lastUpdated: new Date(),
perHourMiningRate: 55
},
{
name: "liquidNitrogen",
amount: 66,
lastUpdated: new Date(),
perHourMiningRate: 66
},
{
name: "hydrogen",
amount: 77,
lastUpdated: new Date(),
perHourMiningRate: 77
},
{
name: "oxygen",
amount: 88,
lastUpdated: new Date(),
perHourMiningRate: 88
},
{
name: "helium3",
amount: 99,
lastUpdated: new Date(),
perHourMiningRate: 99
}
];
updatePlanetResources(planetId, resources);
}
export const getResourcesFromPlanet = async (planetId: ObjectId): Promise<Array<PlayerResource> | null> => {
const planet = await getPlanetById(planetId);
if(!planet) return null;
return planet.resources;
}
export const updatePlanetResources = async (planetId: ObjectId, resources: Array<PlayerResource>) => {
const planets = await Planets();
await planets.updateOne({ _id: planetId }, {
$set: {
resources
}
});
}
export const calculateCurrentAvailableResources = async (planetId: ObjectId): Promise<Array<PlayerResource>> => {
const resources = await getResourcesFromPlanet(planetId);
if(resources === null) return [];
resources.forEach(res => {
const timeDiff = Math.abs((new Date()).getTime() - res.lastUpdated.getTime());
const hours = timeDiff / (1000 * 60 * 60);
const amountToAdd = hours * res.perHourMiningRate;
res.amount += amountToAdd;
res.lastUpdated = new Date();
});
updatePlanetResources(planetId, resources);
return resources;
}

View File

@ -1,8 +1,7 @@
import { ObjectId } from "mongodb"; import type { ObjectId } from "mongodb";
import AccessToken from "../../types/AccessToken"; import type AccessToken from "../../types/AccessToken";
import { getAccessToken } from "../db/accessTokens"; import { getAccessToken } from "../db/accessTokens";
import { getUserById } from "../db/users"; import { getUserById } from "../db/users";
import locationManager from "../classes/managers/LocationManager";
export default async function validateAccessToken(request: Request): Promise<Response | AccessToken> { export default async function validateAccessToken(request: Request): Promise<Response | AccessToken> {
let accessToken = request.url.split("?")[1]?.split("&").filter((x) => x.split("=")[0] === "token")[0].split("=")[1]; let accessToken = request.url.split("?")[1]?.split("&").filter((x) => x.split("=")[0] === "token")[0].split("=")[1];
@ -59,12 +58,5 @@ export default async function validateAccessToken(request: Request): Promise<Res
}), { status: 403 } }), { status: 403 }
); );
return { return response;
type: response.type,
user: locationManager.getUser(user._id),
entropy: response.entropy,
createdAt: response.createdAt,
expiresAt: response.expiresAt,
createdFrom: response.createdFrom
} as AccessToken;
} }

View File

@ -1,10 +1,10 @@
import { randomBytes, createHash } from "crypto"; import { randomBytes, createHash } from "crypto";
import type { APIRoute } from "astro"; import type { APIRoute } from "astro";
import type AccessToken from "../../../types/AccessToken";
import { createAccessToken } from "../../../lib/db/accessTokens"; import { createAccessToken } from "../../../lib/db/accessTokens";
import { getUserByNickOrEmail } from "../../../lib/db/users"; import { getUserByNickOrEmail } from "../../../lib/db/users";
import config from '../../../../config.json'; import config from '../../../../config.json';
import AccessToken from "../../../types/AccessToken"; import type { ObjectId } from "mongodb";
import locationManager from "../../../lib/classes/managers/LocationManager";
export const POST: APIRoute = async({ request }) => { export const POST: APIRoute = async({ request }) => {
const data = await request.json().catch(() => {return new Response( const data = await request.json().catch(() => {return new Response(
@ -53,25 +53,16 @@ export const POST: APIRoute = async({ request }) => {
const now = new Date(); const now = new Date();
const timestamp = Buffer.from(String(Date.now())).toString('base64url'); const timestamp = Buffer.from(String(Date.now())).toString('base64url');
const userEncoded = Buffer.from(userFromDb._id?.toString() ?? "").toString('base64url'); const user = Buffer.from(userFromDb._id?.toString() ?? "").toString('base64url');
const random = randomBytes(16).toString("base64url"); const random = randomBytes(16).toString("base64url");
const randomHashed = createHash("sha256").update(random).digest("hex"); const randomHashed = createHash("sha256").update(random).digest("hex");
const expiresIn = (data.duration ?? 86400) * 1000; const expiresIn = (data.duration ?? 86400) * 1000;
const tokenString = `A.${timestamp}.${userEncoded}.${random}`; const tokenString = `A.${timestamp}.${user}.${random}`;
const user = locationManager.getUser(userFromDb._id);
if(!user) return new Response(
JSON.stringify({
code: 404,
message: "Not found",
error: `User ${data.username} not found`
}), { status: 404 }
)
const accessToken: AccessToken = { const accessToken: AccessToken = {
type: "A", type: "A",
user, user: userFromDb._id as ObjectId,
entropy: randomHashed.toString(), entropy: randomHashed.toString(),
createdAt: now, createdAt: now,
expiresAt: new Date(now.getTime() + expiresIn), expiresAt: new Date(now.getTime() + expiresIn),

View File

@ -1,20 +1,13 @@
import type { APIRoute } from "astro"; import type { APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken"; import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../lib/db/users"; import { getUserByAccessToken } from "../../../lib/db/users";
import type User from "../../../types/User";
export const GET: APIRoute = async({ request }) => { export const GET: APIRoute = async({ request }) => {
const response = await validateAccessToken(request); const response = await validateAccessToken(request);
if(response instanceof Response) return response; if(response instanceof Response) return response;
const user = await getUserByAccessToken(response); const user = (await getUserByAccessToken(response)) as User;
if(!user) return new Response(
JSON.stringify({
code: 404,
message: "Not found",
data: "Access token does not match any user"
}), { status: 404 }
);
return new Response( return new Response(
JSON.stringify({ JSON.stringify({

View File

@ -1,10 +1,13 @@
import { type APIRoute } from "astro"; import { build, type APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken"; import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../lib/db/users"; import { calculateCurrentAvailableResources, updatePlanetResources } from "../../../lib/utils/resourceManager";
import { getUserByAccessToken, getUserResearch } from "../../../lib/db/users";
import Planet from "../../../types/Planet";
import { createOrUpgradeBuilding, getPlanetById } from "../../../lib/db/planets";
import { ObjectId } from "mongodb"; import { ObjectId } from "mongodb";
import locationManager from "../../../lib/classes/managers/LocationManager"; import DBResource from "../../../types/PlayerResource";
import Building from "../../../lib/classes/Building"; import { getAllBuildings } from "../../../lib/db/buildings";
import { getAllResources } from "../../../lib/db/resources"; import DBBuilding from "../../../types/DBBuilding";
export const POST: APIRoute = async({ request }) => { export const POST: APIRoute = async({ request }) => {
const response = await validateAccessToken(request); const response = await validateAccessToken(request);
@ -33,77 +36,120 @@ export const POST: APIRoute = async({ request }) => {
) )
} }
const userPlanet = locationManager.getPlanet(new ObjectId(body.planet)); const buildingId = body.building;
let buildingData: DBBuilding | null = null;
const buildings = await getAllBuildings();
buildingData = buildings.find((element) => element.id === buildingId) ?? null;
if(!buildingData) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid building id"
}), { status: 400 }
)
}
let userPlanet: Planet | null;
try {
userPlanet = await getPlanetById(new ObjectId(body.planetId));
} catch(e) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid planet id"
}), { status: 400 }
)
}
if(!userPlanet) { if(!userPlanet) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
code: 400, code: 404,
message: "Bad Request", message: "Not Found",
error: "Invalid planet ID" error: "Planet not found"
}), { status: 400 } }), { status: 404 }
) )
} }
// check requirements
// buildings
const playerBuildings = userPlanet.buildings;
buildingData.requirements.buildings.forEach((buildingReq: any) => {
if(playerBuildings.filter((building) => building.id === buildingReq.id)[0].level < buildingReq.level) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: `${buildingReq.id} level ${buildingReq.level} required, found ${playerBuildings.filter((building) => building.id === buildingReq.id)[0].level}`
}), { status: 400 }
)
}
});
const buildingId: string = body.building; // research
const playerResearch = await getUserResearch(user);
buildingData.requirements.research.forEach((researchReq: any) => {
if(playerResearch.filter((research) => research.id === researchReq.id)[0].level < researchReq.level) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.filter((research) => research.id === researchReq.id)[0].level}`
}), { status: 400 }
)
}
});
const buildingObj = userPlanet.buildings.buildingsDB.find(b => b.id === buildingId); // resources
const resources = await calculateCurrentAvailableResources(userPlanet._id);
if(!resources) {
return new Response(
JSON.stringify({
code: 500,
message: "Internal Server Error",
error: "Failed to get resources"
}), { status: 500 }
)
}
const playerCurrentResearch = playerResearch.filter((element: any) => element.id === buildingId)[0];
const level = playerCurrentResearch ? playerCurrentResearch.level : 0;
const newResources = structuredClone(resources);
const missingResources: Array<{}> = [];
Object.entries(buildingData.requirements.resources).forEach(([key, value]) => {
const res = resources.filter((element: DBResource) => element.name === value.name)[0];
const cost = playerCurrentResearch ? value.amount * Math.pow((buildingData as {multiplier: number}).multiplier, level) : value.amount;
if(res.amount < cost) {
missingResources.push({
name: key,
required: cost,
available: res.amount
});
return;
}
else newResources.filter((element: DBResource) => element.name === value.name)[0].amount -= cost;
});
if(!buildingObj) return new Response( if(missingResources.length > 0) {
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid building ID"
}), { status: 400 }
)
const building = new Building(userPlanet.buildings, buildingObj, 1);
const requirements = await building.checkRequirements();
const resources = await building.checkRequiredResources((userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1);
if(!requirements.canBuild || !resources) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
code: 400, code: 400,
message: "Bad Request", message: "Bad Request",
error: requirements.error + " | " + resources ? "" : "Not enough resources" data: missingResources
}), { status: 400 } }), { status: 400 }
) )
} }
const resourcesDiff = await userPlanet.resources.getDifference(building.data.requirements.resources.map(res => { await updatePlanetResources(userPlanet._id, newResources);
return { await createOrUpgradeBuilding(userPlanet._id, { id: buildingId, level: level + 1 });
id: res.id,
amount: Math.pow(building.data.multiplier, (userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1) * res.amount
}
}));
const resourceDB = await getAllResources();
const resourcesAfter = resourcesDiff.map(res => {
const data = resourceDB.find(r => r.id === res.id);
if(!data) throw new Error("Resource not found");
return {
id: res.id,
amount: res.amount,
lastUpdated: res.lastUpdated,
perHourMiningRate: res.perHourMiningRate,
data
}
});
userPlanet.resources.update(resourcesAfter);
userPlanet.buildings.addBuilding(building);
await userPlanet.buildings.sync();
await userPlanet.resources.sync();
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
code: 200, code: 200,
message: "OK" message: "OK"
}), { status: 200 } }), { status: 200 }
); )
} }

View File

@ -1,21 +1,17 @@
import { APIRoute } from "astro"; import { APIRoute } from "astro";
import { getAllPlanets } from "../../../lib/db/planets"; import { getAllPlanets } from "../../../lib/db/planets";
import Planet from "../../../types/Planet";
import { ObjectId } from "mongodb"; import { ObjectId } from "mongodb";
import locationManager from "../../../lib/classes/managers/LocationManager";
export const GET: APIRoute = async ({ request }) => { export const GET: APIRoute = async ({ request }) => {
const planets = await getAllPlanets(); const planets = await getAllPlanets();
const response: Array<{planetId: ObjectId, ownerId: ObjectId, name: string}> = []; const response: Array<{planetId: ObjectId, ownerId: ObjectId, name: string}> = [];
planets.forEach(planet => { planets.forEach((planet: Planet) => {
const playerPlanet = locationManager.getPlanet(planet._id);
if(!playerPlanet) return;
response.push({ response.push({
planetId: planet._id, planetId: planet._id,
ownerId: playerPlanet.manager.owner.id, ownerId: planet.owner._id,
name: planet.name, name: planet.name,
}) })
}); });

View File

@ -1,8 +1,10 @@
import { APIRoute } from "astro"; import { APIRoute } from "astro";
import { getPlanetById } from "../../../../lib/db/planets";
import { ObjectId } from "mongodb"; import { ObjectId } from "mongodb";
import Planet from "../../../../types/Planet";
import { calculateCurrentAvailableResources } from "../../../../lib/utils/resourceManager";
import validateAccessToken from "../../../../lib/utils/validateAccessToken"; import validateAccessToken from "../../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../../lib/db/users"; import { getUserByAccessToken } from "../../../../lib/db/users";
import locationManager from "../../../../lib/classes/managers/LocationManager";
export const GET: APIRoute = async ({ params, request }) => { export const GET: APIRoute = async ({ params, request }) => {
const response = await validateAccessToken(request); const response = await validateAccessToken(request);
@ -26,23 +28,36 @@ export const GET: APIRoute = async ({ params, request }) => {
}), { status: 400 } }), { status: 400 }
); );
const planet = locationManager.getPlanet(new ObjectId(planetId)); let planet: Planet | null;
if(!planet) return new Response( try {
JSON.stringify({ planet = await getPlanetById(new ObjectId(planetId));
code: 500, if(!planet) return new Response(
message: "Internal Server Error" JSON.stringify({
}), { status: 500 } code: 404,
); message: "Not Found"
}), { status: 404 }
);
} catch(e) {
return new Response(
JSON.stringify({
code: 500,
message: "Internal Server Error"
}), { status: 500 }
);
}
if(!planet.manager.owner.id.equals(user._id)) return new Response( if(planet.owner._id.toString() !== user._id.toString()) return new Response(
JSON.stringify({ JSON.stringify({
code: 403, code: 403,
message: "Forbidden" message: "Forbidden"
}), { status: 403 } }), { status: 403 }
); );
await locationManager.getPlanet(planet._id)?.resources.calculateCurrentAvailableResources(); await calculateCurrentAvailableResources(planet._id);
//@ts-ignore
delete planet.owner.password;
return new Response( return new Response(
JSON.stringify({ JSON.stringify({

View File

@ -1,7 +1,6 @@
import { type APIRoute } from "astro"; import { type APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken"; import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken, getUserResearch } from "../../../lib/db/users"; import { getUserByAccessToken, getUserResearch } from "../../../lib/db/users";
import locationManager from "../../../lib/classes/managers/LocationManager";
export const GET: APIRoute = async({ request }) => { export const GET: APIRoute = async({ request }) => {
const response = await validateAccessToken(request); const response = await validateAccessToken(request);
@ -17,13 +16,11 @@ export const GET: APIRoute = async({ request }) => {
) )
} }
const userResearch = locationManager.getUser(user._id)?.research.research ?? [];
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
code: 200, code: 200,
message: "OK", message: "OK",
data: userResearch data: await getUserResearch(user)
}), { status: 200 } }), { status: 200 }
) )
} }

View File

@ -1,16 +1,20 @@
import { type APIRoute } from "astro"; import { type APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken"; import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../lib/db/users"; import { createOrUpgradeResearch, getUserByAccessToken, getUserResearch } from "../../../lib/db/users";
import { getPlanetById } from "../../../lib/db/planets";
import Planet from "../../../types/Planet";
import { ObjectId } from "mongodb"; import { ObjectId } from "mongodb";
import { getResearchById } from "../../../lib/db/research"; import { calculateCurrentAvailableResources, getResourcesFromPlanet, updatePlanetResources } from "../../../lib/utils/resourceManager";
import locationManager from "../../../lib/classes/managers/LocationManager"; import PlayerResource from "../../../types/PlayerResource";
import calculateAvailableResources from "../../../lib/utils/calculateAvailableResources";
import { getAllResearch, getResearchById } from "../../../lib/db/research";
export const POST: APIRoute = async({ request }) => { export const POST: APIRoute = async({ request }) => {
const response = await validateAccessToken(request); const response = await validateAccessToken(request);
if(response instanceof Response) return response; if(response instanceof Response) return response;
const userDB = await getUserByAccessToken(response); const user = await getUserByAccessToken(response);
if(userDB === null) { if(user === null) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
code: 401, code: 401,
@ -32,30 +36,8 @@ export const POST: APIRoute = async({ request }) => {
) )
} }
const userPlanet = locationManager.getPlanet(new ObjectId(body.planetId));
if(!userPlanet) {
return new Response(
JSON.stringify({
code: 404,
message: "Not Found",
error: "Planet not found"
}), { status: 404 }
)
}
const user = userPlanet.manager.owner;
if(!user.id.equals(userDB._id)) {
return new Response(
JSON.stringify({
code: 403,
message: "Forbidden",
error: "You don't own this planet"
}), { status: 403 }
)
}
const researchId = body.research as string; const researchId = body.research;
const research = await getResearchById(researchId); const research = await getResearchById(researchId);
if(!research) { if(!research) {
@ -67,27 +49,47 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 400 } }), { status: 400 }
) )
} }
let userPlanet: Planet | null;
try {
userPlanet = await getPlanetById(new ObjectId(body.planetId));
} catch(e) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid planet id"
}), { status: 400 }
)
}
if(!userPlanet) {
return new Response(
JSON.stringify({
code: 404,
message: "Not Found",
error: "Planet not found"
}), { status: 404 }
)
}
// check requirements // check requirements
// buildings // buildings
const buildings = userPlanet.buildings; const buildings = userPlanet.buildings;
for(const buildingReq of research.requirements.buildings) { research.requirements.buildings.forEach((buildingReq) => {
if((buildings.buildings.find((building) => building.data.id === buildingReq.id)?.level ?? 0) < buildingReq.level) { if(buildings.filter((building) => building.id === buildingReq.id)[0]?.level ?? 0 < buildingReq.level) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
code: 400, code: 400,
message: "Bad Request", message: "Bad Request",
error: `${buildingReq.id} level ${buildingReq.level} required, found ${buildings.buildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0}` error: `${buildingReq.id} level ${buildingReq.level} required, found ${buildings.filter((building) => building.id === buildingReq.id)[0]?.level ?? 0}`
}), { status: 400 } }), { status: 400 }
) )
} }
}; });
// research // research
const playerResearch = user.research.research; const playerResearch = await getUserResearch(user);
for(const researchReq of research.requirements.research) { research.requirements.research.forEach((researchReq) => {
if((playerResearch.find((research) => research.id === researchReq.id)?.level ?? 0) < researchReq.level) { if(playerResearch.filter((research) => research.id === researchReq.id)[0].level < researchReq.level) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
code: 400, code: 400,
@ -96,10 +98,10 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 400 } }), { status: 400 }
) )
} }
}; });
// resources // resources
const resources = await userPlanet.resources.calculateCurrentAvailableResources(); const resources = await calculateCurrentAvailableResources(userPlanet._id);
if(!resources) { if(!resources) {
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
@ -109,25 +111,24 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 500 } }), { status: 500 }
) )
} }
const playerCurrentResearch = playerResearch.filter((element: any) => element.id === researchId)[0]; const playerCurrentResearch = playerResearch.filter((element: any) => element.id === researchId)[0];
const level = playerCurrentResearch ? playerCurrentResearch.level : 0; const level = playerCurrentResearch ? playerCurrentResearch.level : 0;
const newResources = structuredClone(resources); const newResources = structuredClone(resources);
const missingResources: Array<{ id: string, required: number, available: number }> = []; const missingResources: Array<{}> = [];
for(const resource of research.requirements.resources) { research.requirements.resources.forEach(resource => {
const res = resources.filter((element) => element.id === resource.id)[0]; const res = resources.filter((element: PlayerResource) => element.name === resource.name)[0];
const cost = playerCurrentResearch ? resource.amount * Math.pow(research.multiplier, level) : resource.amount; const cost = playerCurrentResearch ? resource.amount * Math.pow(research.multiplier, level) : resource.amount;
if(res.amount < cost) { if(res.amount < cost) {
missingResources.push({ missingResources.push({
id: resource.id, name: resource.name,
required: cost, required: cost,
available: res.amount available: res.amount
}); });
break; return;
} }
else newResources.filter((element) => element.id === resource.id)[0].amount -= cost; else newResources.filter((element: PlayerResource) => element.name === resource.name)[0].amount -= cost;
}; });
if(missingResources.length > 0) { if(missingResources.length > 0) {
return new Response( return new Response(
@ -138,16 +139,9 @@ export const POST: APIRoute = async({ request }) => {
}), { status: 400 } }), { status: 400 }
) )
} }
userPlanet.resources.update(newResources);
userPlanet.manager.owner.research.addResearch({
id: researchId,
level: level + 1,
data: research
});
await userPlanet.resources.sync(); await updatePlanetResources(userPlanet._id, newResources);
await userPlanet.manager.owner.research.sync(); await createOrUpgradeResearch(user._id, { id: researchId, level: level + 1 });
return new Response( return new Response(
JSON.stringify({ JSON.stringify({

View File

@ -6,9 +6,7 @@ import { getUserByAccessToken } from '../../lib/db/users';
import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver'; import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver';
import ResourceBar from '../../components/ResourceBar.astro'; import ResourceBar from '../../components/ResourceBar.astro';
import { getAllBuildings } from '../../lib/db/buildings'; import { getAllBuildings } from '../../lib/db/buildings';
import locationManager from '../../lib/classes/managers/LocationManager'; import DBBuilding from '../../types/DBBuilding';
import { ObjectId } from 'mongodb';
import DBBuilding from '../../types/db/DBBuilding';
const buildingsList = await getAllBuildings(); const buildingsList = await getAllBuildings();
@ -23,31 +21,20 @@ const locale = await getHighestWeightedLanguage(Astro.request.headers.get('accep
const lang = await getLocales(locale); const lang = await getLocales(locale);
const planetId = Astro.cookies.get('planetid')?.value ?? "";
if(planetId === "") return "No planet selected";
const planet = locationManager.getPlanet(new ObjectId(planetId));
if(!planet) return "Planet not found";
const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any>, energy: number } } = {}; const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any>, energy: number } } = {};
for(const building of buildingsList) { for(const building of buildingsList) {
modalSet[building.id] = { modalSet[building.id] = {
resources: building.requirements.resources.map(resource => { resources: building.requirements.resources,
return {
id: resource.id,
amount: Math.pow(building.multiplier, (planet.buildings.getBuildingById(building.id)?.level ?? 0) ) * resource.amount
};
}),
research: building.requirements.research, research: building.requirements.research,
buildings: building.requirements.buildings, buildings: building.requirements.buildings,
energy: building.energy energy: building.energy
}; };
} }
const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: Array<DBBuilding & { level: number }> }, building) => { const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: DBBuilding[] }, building) => {
if(!acc[building.category]) acc[building.category] = []; if(!acc[building.category]) acc[building.category] = [];
acc[building.category].push({ ...building, level: planet.buildings.getBuildingById(building.id)?.level ?? 0 }); acc[building.category].push(building);
return acc; return acc;
}, {}); }, {});
--- ---
@ -73,7 +60,6 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: Array<DB
{buildings.map(building => ( {buildings.map(building => (
<BuildingCard <BuildingCard
id={building.id} id={building.id}
level={building.level.toString()}
name={getObj(lang, "buildings", building.id).name} name={getObj(lang, "buildings", building.id).name}
description={getObj(lang, "buildings", building.id).description ?? ""} description={getObj(lang, "buildings", building.id).description ?? ""}
image={`/images/buildings/${building.id}.jpeg`} image={`/images/buildings/${building.id}.jpeg`}
@ -199,7 +185,7 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: Array<DB
color: lime; color: lime;
} }
</style> </style>
<script define:vars={{ modalSet, lang, planetId }}> <script define:vars={{ modalSet, lang }}>
const modalResources = document.getElementById("building-modal-req-resources"); const modalResources = document.getElementById("building-modal-req-resources");
const modalBuildings = document.getElementById("building-modal-req-buildings"); const modalBuildings = document.getElementById("building-modal-req-buildings");
const modalResearch = document.getElementById("building-modal-req-research"); const modalResearch = document.getElementById("building-modal-req-research");
@ -216,7 +202,7 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: Array<DB
const reqResearch = modalSet[el.parentElement.parentElement.dataset.id]?.research ?? []; const reqResearch = modalSet[el.parentElement.parentElement.dataset.id]?.research ?? [];
modalResources.innerHTML = reqResources.length === 0 ? "None" : reqResources.map(resource => { modalResources.innerHTML = reqResources.length === 0 ? "None" : reqResources.map(resource => {
return `${lang['resources'].find(r => r.id === resource.id).name}: ${resource.amount}`; return `${lang['resources'].find(r => r.id === resource.name).name}: ${resource.amount}`;
}).join("<br />"); }).join("<br />");
modalBuildings.innerHTML = reqBuildings.length === 0 ? "None" : reqBuildings.map(building => { modalBuildings.innerHTML = reqBuildings.length === 0 ? "None" : reqBuildings.map(building => {
@ -247,28 +233,21 @@ const buildingsByCategory = buildingsList.reduce((acc: { [key: string]: Array<DB
backgroundDiv.style.display = 'none'; backgroundDiv.style.display = 'none';
}); });
const allButtons = document.getElementsByClassName("item-card-build"); const allButtons = document.getElementsByClassName("a-button");
for(const buildingButton of allButtons) { for(const buildingButton of allButtons) {
buildingButton.addEventListener("click", async () => { buildingButton.addEventListener("click", async () => {
const id = buildingButton.id.split("_")[1]; const id = buildingButton.id.split("_")[1];
const response = await fetch('/api/build/createBuilding', { await fetch('/api/build/createBuilding', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
building: id, building: id
planet: planetId
}) })
}); });
if(response.status === 200) {
window.location.reload();
} else {
alert("Failed to build building: " + JSON.stringify(await response.json()));
}
}); });
} }
</script> </script>

View File

@ -5,6 +5,7 @@ import { getUserByAccessToken } from '../../lib/db/users';
import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver'; import { getHighestWeightedLanguage, getLocales, getName, getObj } from '../../lib/utils/langDriver';
import ResourceBar from '../../components/ResourceBar.astro'; import ResourceBar from '../../components/ResourceBar.astro';
import ItemCard from '../../components/ItemCard.astro'; import ItemCard from '../../components/ItemCard.astro';
import DBResearch from '../../types/DBResearch';
import { getAllResearch } from '../../lib/db/research'; import { getAllResearch } from '../../lib/db/research';
const researchList = await getAllResearch(); const researchList = await getAllResearch();
@ -16,13 +17,23 @@ if(loggedToken === null || username === "") return Astro.redirect('/logout');
const checkUser = await getUserByAccessToken(loggedToken); const checkUser = await getUserByAccessToken(loggedToken);
if(checkUser === null || checkUser.username !== username) return Astro.redirect('/logout'); if(checkUser === null || checkUser.username !== username) return Astro.redirect('/logout');
const planetId = Astro.cookies.get('planetid')?.value ?? "";
if(planetId === "") return Astro.redirect('/logout');
const locale = await getHighestWeightedLanguage(Astro.request.headers.get('accept-language')); const locale = await getHighestWeightedLanguage(Astro.request.headers.get('accept-language'));
const lang = await getLocales(locale); const lang = await getLocales(locale);
// type DBResearchDetails = DBResearch & { level: number };
// const researchDetails: DBResearchDetails[] = [];
// researchList.forEach(element => {
// const userLevel = checkUser.research.find(x => x.id === element.id)?.level ?? 0;
// const tempResDetails: DBResearchDetails = {
// level: userLevel,
// ...element
// }
// researchDetails.push(tempResDetails);
// });
const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any> } } = {}; const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any> } } = {};
for(const research of researchList) { for(const research of researchList) {
@ -55,7 +66,6 @@ for(const research of researchList) {
<ItemCard <ItemCard
id={research.id} id={research.id}
name={getObj(lang, "research", research.id).name} name={getObj(lang, "research", research.id).name}
level={checkUser.research.find(x => x.id === research.id)?.level.toString() ?? "0"}
description={getObj(lang, "research", research.id).description ?? ""} description={getObj(lang, "research", research.id).description ?? ""}
image={`/images/research/${research.id}.jpeg`} image={`/images/research/${research.id}.jpeg`}
button_type="general" button_type="general"
@ -177,7 +187,7 @@ for(const research of researchList) {
z-index: 101; z-index: 101;
} }
</style> </style>
<script define:vars={{ modalSet, lang, planetId }}> <script define:vars={{ modalSet, lang }}>
const modalResources = document.getElementById("research-modal-req-resources"); const modalResources = document.getElementById("research-modal-req-resources");
const modalBuildings = document.getElementById("research-modal-req-buildings"); const modalBuildings = document.getElementById("research-modal-req-buildings");
const modalResearch = document.getElementById("research-modal-req-research"); const modalResearch = document.getElementById("research-modal-req-research");
@ -227,28 +237,23 @@ for(const research of researchList) {
backgroundDiv.style.display = 'none'; backgroundDiv.style.display = 'none';
}); });
const allButtons = document.getElementsByClassName("item-card-build"); const allButtons = document.getElementsByClassName("a-button");
for(const researchButton of allButtons) { for(const researchButton of allButtons) {
researchButton.addEventListener("click", async () => { researchButton.addEventListener("click", async () => {
const id = researchButton.id.split("_")[1]; const id = researchButton.id.split("_")[1];
const response = await fetch('/api/research/performResearch', { console.log(id);
await fetch('/api/research/performResearch', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
research: id, research: id
planetId
}) })
}); });
if(response.status === 200) {
window.location.reload();
} else {
alert("Failed to build building: " + JSON.stringify(await response.json()));
}
}); });
} }
</script> </script>

View File

@ -2,12 +2,11 @@
import Layout from '../layouts/Layout.astro'; import Layout from '../layouts/Layout.astro';
import NavBar from '../components/NavBar.astro'; import NavBar from '../components/NavBar.astro';
import { getUserByNickOrEmail, updateLastLogin } from '../lib/db/users'; import { getUserByNickOrEmail, getUserWithPlanets, updateLastLogin } from '../lib/db/users';
import config from '../../config.json'; import config from '../../config.json';
import { verify } from 'argon2'; import { verify } from 'argon2';
import locationManager from '../lib/classes/managers/LocationManager';
let error = ""; let error = "";
@ -26,11 +25,9 @@ if(Astro.request.method === "POST") {
Astro.redirect("/login"); Astro.redirect("/login");
} }
const userDB = await getUserByNickOrEmail(username as string); const user = await getUserByNickOrEmail(username as string);
if(userDB !== null && await verify(userDB.password, password as string)) { if(user !== null && await verify(user.password, password as string)) {
const user = locationManager.getUser(userDB._id);
if(!user) throw new Error("User not found");
const sessionTime = config.SESSION_TIME_MINUTES * 60; const sessionTime = config.SESSION_TIME_MINUTES * 60;
const res = await fetch(`http://localhost:4321/api/auth/generateAccessToken`, { const res = await fetch(`http://localhost:4321/api/auth/generateAccessToken`, {
@ -64,14 +61,18 @@ if(Astro.request.method === "POST") {
secure: true secure: true
}); });
Astro.cookies.set("userid", user.id?.toString() as string, { Astro.cookies.set("userid", user._id?.toString() as string, {
path: "/", path: "/",
maxAge: sessionTime, maxAge: sessionTime,
sameSite: "lax", sameSite: "lax",
secure: true secure: true
}); });
Astro.cookies.set("planetid", user.mainPlanet._id, { const userWithPlanets = await getUserWithPlanets(user._id);
if(!userWithPlanets) return;
const planetId = userWithPlanets.planets.data[0]._id;
Astro.cookies.set("planetid", planetId.toString(), {
path: "/", path: "/",
maxAge: sessionTime, maxAge: sessionTime,
sameSite: "lax", sameSite: "lax",

View File

@ -1,10 +1,11 @@
import User from "../lib/classes/User"; import type { ObjectId } from "mongodb";
export default interface AccessToken { export default interface AccessToken {
type: string; _id?: ObjectId;
user: User | null; type: "A" | "X";
user: ObjectId | null;
entropy: string; entropy: string;
createdAt: Date; createdAt: Date;
expiresAt: Date | null; expiresAt: Date | null;
createdFrom: string | null; createdFrom: null | 'loginForm';
} }

4
src/types/Building.ts Normal file
View File

@ -0,0 +1,4 @@
export default interface Building {
id: string;
level: number;
}

11
src/types/DBBuilding.ts Normal file
View File

@ -0,0 +1,11 @@
export default interface DBBuilding {
id: string,
category: string,
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ name: string, amount: number }>,
},
energy: number,
multiplier: number,
}

10
src/types/DBResearch.ts Normal file
View File

@ -0,0 +1,10 @@
export default interface DBResearch {
id: string,
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ name: string, amount: number }>,
},
time: number,
multiplier: number,
}

5
src/types/DBResource.ts Normal file
View File

@ -0,0 +1,5 @@
export default interface DBResource {
id: string,
type: string,
icon: string
}

20
src/types/DBShip.ts Normal file
View File

@ -0,0 +1,20 @@
export default interface DBShip {
id: string,
capacity: {
solid: number,
liquid: number,
gas: number,
},
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ name: string, amount: number }>,
},
time: number,
structure: {
hitpoints: number,
defense: number,
attack: number,
},
speed: number,
}

15
src/types/Planet.ts Normal file
View File

@ -0,0 +1,15 @@
import type { ObjectId } from "mongodb";
import type PlayerResource from "./PlayerResource";
import type Building from "./Building";
import type Ship from "./Ship";
import type User from "./User";
export default interface Planet {
_id: ObjectId,
owner: User,
name: string,
fields: number,
resources: Array<PlayerResource>,
buildings: Array<Building>,
ships: Array<Ship>,
}

View File

@ -0,0 +1,6 @@
import type Resource from "./Resource";
export default interface DBResource extends Resource {
lastUpdated: Date;
perHourMiningRate: number;
}

4
src/types/Research.ts Normal file
View File

@ -0,0 +1,4 @@
export default interface Research {
id: string;
level: number;
}

4
src/types/Resource.ts Normal file
View File

@ -0,0 +1,4 @@
export default interface Resource {
name: string;
amount: number;
}

13
src/types/Resources.ts Normal file
View File

@ -0,0 +1,13 @@
export default interface Resources {
coal: number;
iron: number;
gold: number;
water: number;
sulfuricAcid: number;
liquidNitrogen: number;
hydrogen: number;
oxygen: number;
helium3: number;
}

4
src/types/Ship.ts Normal file
View File

@ -0,0 +1,4 @@
export default interface Ship {
id: string;
amount: number;
}

18
src/types/User.ts Normal file
View File

@ -0,0 +1,18 @@
import type { ObjectId } from "mongodb";
import type Research from "./Research";
import type Planet from "./Planet";
export default interface User {
_id: ObjectId;
username: string;
email: string;
password: string;
lastLogin: Date;
research: Array<Research>;
planets: {
partial: boolean;
data: Array<Planet>;
};
createdAt: Date;
updatedAt: Date;
}

View File

@ -1,11 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBAccessToken {
_id: ObjectId;
type: string;
user: ObjectId;
entropy: string;
createdAt: Date;
expiresAt: Date;
createdFrom: string | null;
}

View File

@ -1,15 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBBuilding {
_id: ObjectId;
id: string;
category: string;
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ id: string, amount: number }>,
};
energy: number;
time: number;
multiplier: number;
}

View File

@ -1,11 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBFleet {
_id: ObjectId;
source: ObjectId;
destination: ObjectId;
finished: boolean;
returning: boolean;
mission: string;
ships: Array<{ id: string, amount: number }>;
}

View File

@ -1,7 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBGalaxy {
_id: ObjectId;
name: string;
sectors: Array<ObjectId>;
}

View File

@ -1,10 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBPlanet {
_id: ObjectId;
name: string;
fields: number;
resources: Array<{ id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }>;
buildings: Array<{ id: string, level: number }>;
ships: Array<{ id: string, amount: number }>;
}

View File

@ -1,13 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBResearch {
_id: ObjectId;
id: string;
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ id: string, amount: number }>,
};
time: number;
multiplier: number;
}

View File

@ -1,8 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBResource {
_id: ObjectId;
id: string;
type: string;
icon: string;
}

View File

@ -1,8 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBSector {
_id: ObjectId;
name: string;
expedition: any; // TODO: Define expedition type
systems: Array<ObjectId>;
}

View File

@ -1,23 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBShip {
_id: ObjectId;
id: string;
capacity: {
solid: number;
liquid: number;
gas: number;
};
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ id: string, amount: number }>,
};
time: number;
structure: {
hitpoints: number;
defense: number;
attack: number;
};
speed: number;
}

View File

@ -1,8 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBSystem {
_id: ObjectId;
name: string;
ownedBy: ObjectId;
planets: Array<ObjectId>;
}

View File

@ -1,13 +0,0 @@
import { ObjectId } from "mongodb";
export default interface DBUser {
_id: ObjectId;
username: string;
email: string;
password: string;
lastLogin: Date;
createdAt: Date;
updatedAt: Date;
research: Array<{ id: string, level: number }>;
mainPlanet: ObjectId;
}

31
test/database.test.ts Normal file
View File

@ -0,0 +1,31 @@
import { createUser, deleteUser, getUserByNickOrEmail } from '../src/lib/db/users';
import { connect, disconnect } from '../src/lib/db/mongodb';
import { verify } from 'argon2';
beforeEach(async () => {
await connect();
});
afterEach(async () => {
await disconnect();
});
describe('Test user database functions', () => {
test('getUserByNickOrEmail', async () => {
const user = await getUserByNickOrEmail("gargamel");
expect(user).not.toBeNull();
expect(user?.username).toBe("gargamel");
expect(user?.email).toBe("gargamel@smerfy.pl");
});
test('createAndVerifyUser', async () => {
await createUser("test", "test@example.com", "password");
const user = await getUserByNickOrEmail("test");
expect(user).not.toBeNull();
if(!user) return;
expect(user?.username).toBe("test");
expect(user?.email).toBe("test@example.com");
expect(await verify(user?.password as string, "password")).toBe(true);
await deleteUser(user._id);
});
});

View File

@ -0,0 +1,85 @@
import { connect, disconnect } from "../src/lib/db/mongodb";
import { createUser, deleteUser } from "../src/lib/db/users";
import { calculateCurrentAvailableResources, getUserResources, updateUserResources } from "../src/lib/utils/resourceManager";
import User from "../src/types/User";
let user: User;
describe('Test resource manager', () => {
beforeEach(async () => {
await connect();
user = await createUser("test", "test@example.com", "password");
});
afterEach(async () => {
await deleteUser(user._id);
await disconnect();
});
describe('Test initial resources amount', () => {
test('initialResourcesAmount', async () => {
const resources = await getUserResources(user._id);
let i = 1;
resources.forEach(res => {
expect(res.amount).toBe(i * 11);
expect(res.perHourMiningRate).toBe(i * 11);
i++;
});
});
});
describe('Test calculation of available resources', () => {
test('calculationOfAvailableResources', async () => {
const resources = await getUserResources(user._id);
resources.forEach(res => {
res.amount = 0;
res.lastUpdated = new Date(Date.now() - 1000 * 60 * 60);
});
await updateUserResources(user._id, resources);
const availableResources = await calculateCurrentAvailableResources(user._id);
let i = 1;
availableResources.forEach(res => {
expect(res.amount).toBeCloseTo(i * 11);
i++;
});
const resourcesAfter = await getUserResources(user._id);
i = 1;
resourcesAfter.forEach(res => {
expect(res.amount).toBeCloseTo(i * 11);
i++;
});
});
});
describe('Test taking resources', () => {
test('takingResources', async () => {
const resources = await getUserResources(user._id);
resources.forEach(res => {
res.amount = 10_000;
});
let i = 1;
resources.forEach(res => {
res.amount -= i * 100;
i++;
});
await updateUserResources(user._id, resources);
const resourcesAfter = await getUserResources(user._id);
i = 1;
resourcesAfter.forEach(res => {
expect(res.amount).toBeCloseTo(10_000 - i * 100);
i++;
});
});
});
});