Recoded the dbUser and its collection class. StoredPlayer and StoredPlayerCollection are also finished
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
- Make a whole new class for each model called "Player", "DbUser", etc. and wrap mongoose completely.
|
||||
- Create a corresponding namespace "PlayerCollection", "DbUserCollection", etc. for the database model itself and its
|
||||
corresponding methods.
|
||||
- Decide on whether to always use the namespace to call methods and pass an id, with the returned objects "Player",
|
||||
"DbUser", etc. all being immutable object copies with limited attributes and no methods, or actually create extra
|
||||
classes that have methods that can be called (right now thinking the first idea is better).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import DbUser, {IDbUser, IDbUserDoc} from "../models/dbUser";
|
||||
import DbUser, {IDbUser, IDbUserDoc} from "../models/dbUser_old";
|
||||
import {RequestHandler} from "express";
|
||||
import {IPlayer} from "../models/player";
|
||||
import {IPlayer} from "../models/StoredPlayer";
|
||||
|
||||
export const whoAmI: RequestHandler = async (req, res) => {
|
||||
if (req.isAuthenticated()) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import passport from "passport";
|
||||
import DbUser from "../models/dbUser";
|
||||
import DbUser from "../models/dbUser_old";
|
||||
import {RequestHandler} from "express";
|
||||
|
||||
export const showLoginPage: RequestHandler = (req, res) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import DbUser, { IDbUser } from "../models/dbUser";
|
||||
import DbUser, { IDbUser } from "../models/dbUser_old";
|
||||
import { RequestHandler } from "express";
|
||||
import Player, { IPlayer } from "../models/player";
|
||||
import Player, { IPlayer } from "../models/StoredPlayer";
|
||||
|
||||
const DEFAULT_RULESET = "DEFAULT_RULESET";
|
||||
const UPPER_BONUS_THRESHOLD = 63;
|
||||
|
||||
@@ -39,6 +39,7 @@ app.locals = {
|
||||
initialisePassport();
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
app.use(Settings.serverRoot + "/static", express.static("static"));
|
||||
app.use(Settings.serverRoot, MainRouter);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "./stats";
|
||||
import {
|
||||
getMongoObjectCollection,
|
||||
MongoStoredObject, MongoStoredObjectCollection, StoredObject, StoredObjectCollection,
|
||||
MongoStoredObject, MongoStoredObjectCollection, StoredObject, StoredObjectCollection, tryQuery,
|
||||
} from "./utils";
|
||||
import {CellValue} from "../controllers/statsController";
|
||||
import mongo from "mongodb";
|
||||
@@ -24,22 +24,22 @@ export interface StoredPlayerData {
|
||||
}
|
||||
|
||||
interface StoredPlayerCollection extends StoredObjectCollection<StoredPlayer> {
|
||||
findById(id: string): Promise<StoredPlayer>;
|
||||
}
|
||||
|
||||
class MongoStoredPlayerCollection
|
||||
extends MongoStoredObjectCollection<MongoStoredPlayer, StoredPlayer>
|
||||
extends MongoStoredObjectCollection<StoredPlayerData, StoredPlayer>
|
||||
implements StoredPlayerCollection {
|
||||
|
||||
private updater: PlayerStatsUpdater;
|
||||
constructor(collectionClient: mongo.Collection) {
|
||||
super(collectionClient);
|
||||
super(collectionClient, MongoStoredPlayer);
|
||||
this.updater = new PlayerStatsUpdater();
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<StoredPlayer> {
|
||||
const data = await this.findObjectById(id);
|
||||
return new MongoStoredPlayer(data);
|
||||
newPlayer(nick: string): Promise<StoredPlayer> {
|
||||
return tryQuery(async () => {
|
||||
return this.create({nick});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export interface StoredPlayer extends StoredObject {
|
||||
updateStats(results: PlayerGameResults & {outcome: OutcomeType}, ruleset: Ruleset): Promise<void>;
|
||||
}
|
||||
|
||||
class MongoStoredPlayer extends MongoStoredObject<StoredPlayerData> implements StoredPlayer {
|
||||
export class MongoStoredPlayer extends MongoStoredObject<StoredPlayerData> implements StoredPlayer {
|
||||
constructor(data: StoredPlayerData) {
|
||||
super(data);
|
||||
}
|
||||
215
src/models/StoredUser.ts
Normal file
215
src/models/StoredUser.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import {SupportedLang} from "../enums";
|
||||
import Player, {MongoStoredPlayer, StoredPlayer, StoredPlayerData} from "./StoredPlayer";
|
||||
import {AccountStats} from "./stats";
|
||||
import {SavedGameData, StoredSavedGame} from "./savedGame";
|
||||
import {
|
||||
GenericModelError, getMongoObjectCollection,
|
||||
MongoStoredObject,
|
||||
MongoStoredObjectCollection,
|
||||
StoredObject,
|
||||
StoredObjectCollection,
|
||||
StoredObjectId,
|
||||
tryQuery
|
||||
} from "./utils";
|
||||
import mongo from "mongodb";
|
||||
import StoredPlayers from "./StoredPlayer";
|
||||
import bcrypt from "bcrypt";
|
||||
|
||||
export class CredentialsTakenError extends Error {
|
||||
public emailExists: boolean;
|
||||
public usernameExists: boolean;
|
||||
constructor(usernameExists: boolean, emailExists: boolean) {
|
||||
super("Registration failure:" + usernameExists + emailExists);
|
||||
this.usernameExists = usernameExists;
|
||||
this.emailExists = emailExists;
|
||||
this.name = "CredentialsTakenError";
|
||||
}
|
||||
}
|
||||
|
||||
export interface StoredUserData {
|
||||
_id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
lang: SupportedLang;
|
||||
friends: string[];
|
||||
player: StoredObjectId;
|
||||
guests: StoredObjectId[];
|
||||
accountStats: AccountStats;
|
||||
savedGames: SavedGameData[];
|
||||
}
|
||||
|
||||
export interface StoredUser extends StoredObject {
|
||||
getLoginDetails(): Promise<LoginDetails>
|
||||
preferredLang(): Promise<SupportedLang>;
|
||||
getFriends(): Promise<StoredUser[]>;
|
||||
getGuests(): Promise<StoredPlayer[]>;
|
||||
getAccountStats(): Promise<AccountStats>;
|
||||
getSavedGames(): Promise<StoredSavedGame[]>;
|
||||
getMainPlayerInfo(): Promise<StoredPlayer>;
|
||||
findGuestByNick(nick: string): Promise<StoredPlayer | null>;
|
||||
changeLang(lang: SupportedLang): Promise<void>;
|
||||
addGame(game: any): Promise<void>;
|
||||
getGuestById(guestId: string): Promise<StoredPlayer>;
|
||||
addGuest(nick: string): Promise<StoredPlayer>;
|
||||
updateGuest(guestParams: GuestUpdateParams): Promise<StoredPlayer>;
|
||||
deleteGuest(guestId: string): Promise<StoredPlayer | null>;
|
||||
}
|
||||
|
||||
type GuestUpdateParams = { id: string, newNick: string };
|
||||
type LoginDetails = { username: string, email: string, password: string };
|
||||
|
||||
class MongoStoredUser extends MongoStoredObject<StoredUserData> implements StoredUser {
|
||||
constructor(data: StoredUserData) {
|
||||
super(data);
|
||||
}
|
||||
|
||||
async getLoginDetails(): Promise<LoginDetails> {
|
||||
return {username: this.data.username, email: this.data.email, password: this.data.password};
|
||||
}
|
||||
|
||||
async preferredLang(): Promise<SupportedLang> {
|
||||
return this.data.lang;
|
||||
}
|
||||
|
||||
async getFriends(): Promise<StoredUser[]> {
|
||||
const friends: StoredUser[] = [];
|
||||
for (const friendId in this.data.guests) {
|
||||
const foundFriend = await StoredUsers.findById(friendId) as StoredUser;
|
||||
friends.push(foundFriend);
|
||||
}
|
||||
return friends;
|
||||
}
|
||||
|
||||
async getGuests(): Promise<StoredPlayer[]> {
|
||||
const guests: StoredPlayer[] = [];
|
||||
for (const guestId in this.data.guests) {
|
||||
const foundGuest = await StoredPlayers.findById(guestId) as StoredPlayer;
|
||||
guests.push(foundGuest);
|
||||
}
|
||||
return guests;
|
||||
}
|
||||
|
||||
async getAccountStats(): Promise<AccountStats> {
|
||||
return this.data.accountStats;
|
||||
}
|
||||
|
||||
async getSavedGames(): Promise<StoredSavedGame[]> {
|
||||
return this.data.savedGames.map(savedGame => new MongoStoredSavedGame(savedGame));
|
||||
}
|
||||
|
||||
async getMainPlayerInfo(): Promise<StoredPlayer> {
|
||||
return StoredPlayers.findById(this.data.player) as Promise<StoredPlayer>;
|
||||
}
|
||||
|
||||
async findGuestByNick(nick: string): Promise<StoredPlayer | null> {
|
||||
const guests = await this.getGuests();
|
||||
for (const guest of guests) {
|
||||
if (guest.nick() == nick) {
|
||||
return guest;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async changeLang(lang: SupportedLang): Promise<void> {
|
||||
this.data.lang = lang;
|
||||
}
|
||||
|
||||
async addGame(game: SavedGameData): Promise<void> {
|
||||
this.data.savedGames.push(game);
|
||||
}
|
||||
|
||||
getGuestById(guestId: string): Promise<StoredPlayer> {
|
||||
return StoredPlayers.findById(guestId) as Promise<StoredPlayer>;
|
||||
}
|
||||
|
||||
async addGuest(nick: string): Promise<StoredPlayer> {
|
||||
const newGuest = await StoredPlayers.newPlayer(nick);
|
||||
this.data.guests.push(newGuest.id());
|
||||
return newGuest;
|
||||
}
|
||||
|
||||
async updateGuest(guestParams: GuestUpdateParams): Promise<StoredPlayer> {
|
||||
const guest = await StoredPlayers.findById(guestParams.id) as StoredPlayer;
|
||||
await guest.setNick(guestParams.newNick);
|
||||
await StoredPlayers.save(guest);
|
||||
return guest;
|
||||
}
|
||||
|
||||
async deleteGuest(guestId: string): Promise<StoredPlayer | null> {
|
||||
return StoredPlayers.deleteById(guestId);
|
||||
}
|
||||
}
|
||||
|
||||
export interface StoredUserCollection extends StoredObjectCollection<StoredUser> {
|
||||
findByEmail(emailQuery: string): Promise<StoredUser | null>;
|
||||
registerUser(loginDetails: LoginDetails): Promise<StoredUser>;
|
||||
userWithEmailExists(email: string): Promise<boolean>;
|
||||
userWithUsernameExists(username: string): Promise<boolean>;
|
||||
getSerializedAuthUser(id: string): Promise<StoredUser>;
|
||||
}
|
||||
|
||||
class MongoStoredUserCollection extends MongoStoredObjectCollection<StoredUserData, StoredUser> implements StoredUserCollection {
|
||||
constructor(collectionClient: mongo.Collection) {
|
||||
super(collectionClient, MongoStoredUser);
|
||||
}
|
||||
|
||||
findById(id: string): Promise<StoredUser | null> {
|
||||
return this.findObjectById(id);
|
||||
}
|
||||
|
||||
findByEmail(emailQuery: string): Promise<StoredUser | null> {
|
||||
return tryQuery(async () =>
|
||||
await this.findObjectByAttribute("email", emailQuery)
|
||||
);
|
||||
}
|
||||
|
||||
private async addNewUser(loginDetails: LoginDetails): Promise<StoredUser> {
|
||||
const newPlayer = await StoredPlayers.newPlayer(loginDetails.username);
|
||||
return this.create({
|
||||
username: loginDetails.username,
|
||||
email: loginDetails.email,
|
||||
password: loginDetails.password,
|
||||
lang: SupportedLang.gb,
|
||||
player: newPlayer.id()
|
||||
});
|
||||
}
|
||||
|
||||
async registerUser(loginDetails: LoginDetails): Promise<StoredUser> {
|
||||
const usernameTaken = await this.userWithUsernameExists(loginDetails.username);
|
||||
const emailTaken = await this.userWithEmailExists(loginDetails.email);
|
||||
if (usernameTaken || emailTaken) {
|
||||
throw new CredentialsTakenError(usernameTaken, emailTaken);
|
||||
}
|
||||
else {
|
||||
const hashedPassword = await bcrypt.hash(loginDetails.password, 10);
|
||||
return tryQuery(() =>
|
||||
this.addNewUser({...loginDetails, password: hashedPassword})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async userWithEmailExists(email: string): Promise<boolean> {
|
||||
const object = await this.findObjectByAttribute("email", email);
|
||||
return object !== null;
|
||||
}
|
||||
|
||||
async userWithUsernameExists(username: string): Promise<boolean> {
|
||||
const object = await this.findObjectByAttribute("username", username);
|
||||
return object !== null;
|
||||
}
|
||||
|
||||
async getSerializedAuthUser(id: string): Promise<StoredUser> {
|
||||
const dbResult = await this.findById(id);
|
||||
if (dbResult) {
|
||||
return dbResult;
|
||||
}
|
||||
else {
|
||||
throw new GenericModelError("User not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const StoredUsers = new MongoStoredUserCollection(getMongoObjectCollection("users"));
|
||||
export default StoredUsers;
|
||||
@@ -1,263 +0,0 @@
|
||||
import mongoose, {Model} from "mongoose";
|
||||
import Player, {IPlayer, IPlayerDoc, PlayerSchema} from "./player";
|
||||
import {AccountStatsSchema, IAccountStats, IAccountStatsDoc} from "./stats";
|
||||
import SavedGame, {ISavedGame, ISavedGameDoc, SavedGameSchema} from "./savedGame";
|
||||
import bcrypt from "bcrypt";
|
||||
import {SupportedLang} from "../enums";
|
||||
import {GenericModelError, globalSchemaOptions, ModelParameterError, tryQuery} from "./utils";
|
||||
import {GameSubmission} from "../controllers/statsController";
|
||||
|
||||
export class CredentialsTakenError extends Error {
|
||||
public emailExists: boolean;
|
||||
public usernameExists: boolean;
|
||||
constructor(usernameExists: boolean, emailExists: boolean) {
|
||||
super("Registration failure:" + usernameExists + emailExists);
|
||||
this.usernameExists = usernameExists;
|
||||
this.emailExists = emailExists;
|
||||
this.name = "CredentialsTakenError";
|
||||
}
|
||||
}
|
||||
|
||||
export interface IDbUser {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
lang?: SupportedLang;
|
||||
friends?: IDbUser[];
|
||||
player?: IPlayer;
|
||||
guests?: IPlayer[];
|
||||
accountStats?: IAccountStats;
|
||||
savedGames?: ISavedGame[];
|
||||
getMainPlayerInfo(): Promise<IPlayer>;
|
||||
findGuestByNick(nick: string): Promise<IPlayer | null>;
|
||||
changeLang(lang: SupportedLang): void;
|
||||
addGame(game: any): Promise<string | null>;
|
||||
getGuests(): Promise<IPlayer[]>;
|
||||
getGuest(guestId: string): Promise<IPlayer>;
|
||||
addGuest(nick: string): Promise<IPlayer>;
|
||||
updateGuest(guestParams: GuestUpdateParams): Promise<IPlayer>;
|
||||
deleteGuest(guestId: string): Promise<IPlayer>;
|
||||
}
|
||||
|
||||
type GuestUpdateParams = {id: string, newNick: string};
|
||||
|
||||
export interface IDbUserDoc extends mongoose.Document {
|
||||
id: string;
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
lang: SupportedLang;
|
||||
friends: IDbUserDoc[];
|
||||
player: IPlayerDoc;
|
||||
guests: IPlayerDoc[];
|
||||
accountStats: IAccountStatsDoc;
|
||||
savedGames: mongoose.Types.Array<ISavedGameDoc>;
|
||||
getMainPlayerInfo(): Promise<IPlayer>;
|
||||
findGuestByNick(nick: string): Promise<IPlayer | null>;
|
||||
changeLang(lang: SupportedLang): void;
|
||||
addGame(game: any): Promise<string | null>;
|
||||
getGuests(): Promise<IPlayer[]>;
|
||||
getGuest(guestId: string): Promise<IPlayer>;
|
||||
addGuest(nick: string): Promise<IPlayer>;
|
||||
updateGuest(guestParams: GuestUpdateParams): Promise<IPlayer>;
|
||||
deleteGuest(guestId: string): Promise<IPlayer>;
|
||||
}
|
||||
|
||||
export interface IDbUserModel extends mongoose.Model<IDbUserDoc> {
|
||||
findByEmail(emailQuery: string): IDbUserDoc;
|
||||
addNewUser(user: IDbUser): IDbUserDoc;
|
||||
registerUser(username: string, email: string, password: string): IDbUserDoc;
|
||||
userWithEmailExists(email: string): Promise<boolean>;
|
||||
userWithUsernameExists(username: string): Promise<boolean>;
|
||||
getSerializedAuthUser(id: string): Promise<IDbUser>;
|
||||
incrementTimesNoWinner(id: string): Promise<void>;
|
||||
incrementGamesPlayed(id: string): Promise<void>;
|
||||
}
|
||||
|
||||
export const DbUserSchema = new mongoose.Schema({
|
||||
username: { type: String, required: true, unique: true },
|
||||
email: { type: String, required: true, unique: true },
|
||||
password: { type: String, required: true },
|
||||
lang: { type: String, required: true },
|
||||
friends: {type: [mongoose.Schema.Types.ObjectId], default: []},
|
||||
player: {type: PlayerSchema, required: true, unique: true},
|
||||
guests: {type: [PlayerSchema], default: []},
|
||||
accountStats: {type: AccountStatsSchema, default: () => ({}) },
|
||||
savedGames: {type: [SavedGameSchema], default: []},
|
||||
}, {...globalSchemaOptions});
|
||||
|
||||
DbUserSchema.statics.findByEmail = async function (emailQuery: string) {
|
||||
return tryQuery(() =>
|
||||
this.findOne({email: emailQuery})
|
||||
);
|
||||
};
|
||||
|
||||
DbUserSchema.statics.addNewUser = async function (username: string, email: string, hashedPw: string) {
|
||||
const player = new Player( { nick: username });
|
||||
return tryQuery(() =>
|
||||
this.create({
|
||||
username: username,
|
||||
email: email,
|
||||
password: hashedPw,
|
||||
lang: SupportedLang.gb,
|
||||
player
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
DbUserSchema.statics.registerUser = async function (username: string, email: string, password: string) {
|
||||
const usernameTaken = await this.userWithUsernameExists(username);
|
||||
const emailTaken = await this.userWithEmailExists(email);
|
||||
if (usernameTaken || emailTaken) {
|
||||
throw new CredentialsTakenError(usernameTaken, emailTaken);
|
||||
}
|
||||
else {
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
return tryQuery(() =>
|
||||
this.addNewUser(username, email, hashedPassword)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
DbUserSchema.statics.userWithEmailExists = async function (email: string): Promise<boolean> {
|
||||
return tryQuery(() => this.exists({email}));
|
||||
};
|
||||
|
||||
DbUserSchema.statics.userWithUsernameExists = async function (username: string): Promise<boolean> {
|
||||
return tryQuery(() => this.exists({username}));
|
||||
};
|
||||
|
||||
DbUserSchema.statics.getSerializedAuthUser = async function (id: string): Promise<IDbUser> {
|
||||
return tryQuery(() => {
|
||||
return DbUser.findById(id, {id: 1, username: 1, password: 1, lang: 1, email: 1});
|
||||
});
|
||||
};
|
||||
|
||||
DbUserSchema.statics.incrementTimesNoWinner = async function (id: string): Promise<void> {
|
||||
return tryQuery(() => {
|
||||
return DbUser.findById(id, {id: 1, username: 1, password: 1, lang: 1, email: 1});
|
||||
});
|
||||
};
|
||||
|
||||
DbUserSchema.statics.incrementGamesPlayed = async function (id: string): Promise<void> {
|
||||
return tryQuery(async () => {
|
||||
const user = await DbUser.findById(id);
|
||||
user.accountStats.gamesPlayed =
|
||||
});
|
||||
};
|
||||
|
||||
DbUserSchema.methods.getGuests = async function (this: IDbUser): Promise<IPlayer[]> {
|
||||
const user: IDbUserDoc = await tryQuery(async () => {
|
||||
return DbUser.findById(this.id, {"guests.nick": 1, "guests._id": 1}).exec();
|
||||
});
|
||||
return user.guests;
|
||||
};
|
||||
|
||||
DbUserSchema.methods.getGuest = async function (this: IDbUser, guestId: string): Promise<IPlayer> {
|
||||
return tryQuery(async () => {
|
||||
const user = await DbUser.findById(this.id, {guests: {$elemMatch: {_id: guestId}}});
|
||||
if (user!.guests.length > 0) {
|
||||
return user!.guests[0];
|
||||
}
|
||||
else {
|
||||
throw new ModelParameterError("Guest with ID " + guestId + " doesn't exist!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
DbUserSchema.methods.findGuestByNick = async function (this: IDbUser, guestNick: string): Promise<IPlayer | null> {
|
||||
return tryQuery(async () => {
|
||||
const user = await DbUser.findById(this.id, {guests: {$elemMatch: {nick: guestNick}}});
|
||||
if (user!.guests.length > 0) {
|
||||
return user!.guests[0];
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
DbUserSchema.methods.addGuest = async function (this: IDbUser, newGuestNick: string): Promise<IPlayer> {
|
||||
if (this.username !== newGuestNick) {
|
||||
const guestLookup = await this.findGuestByNick(newGuestNick);
|
||||
if (!guestLookup) {
|
||||
return saveGuest(this, newGuestNick);
|
||||
}
|
||||
else {
|
||||
throw new ModelParameterError(`Cannot add a guest with the same name of another guest in this account.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new ModelParameterError("Cannot add a guest with the same name as the account holder's username.")
|
||||
}
|
||||
};
|
||||
|
||||
async function saveGuest(user: IDbUser, newGuestNick: string): Promise<IPlayer> {
|
||||
const newGuest: IPlayerDoc = new Player();
|
||||
newGuest.nick = newGuestNick;
|
||||
await tryQuery(() => {
|
||||
DbUser.findByIdAndUpdate(user.id, {$push: {guests: newGuest}}).exec();
|
||||
});
|
||||
return newGuest;
|
||||
}
|
||||
|
||||
DbUserSchema.methods.updateGuest = async function (this: IDbUser, guestParams: GuestUpdateParams): Promise<IPlayer> {
|
||||
return tryQuery(async () => {
|
||||
const user = await DbUser.findById(this.id);
|
||||
const updatableGuest = user!.guests.find(guest => guest.id === guestParams.id);
|
||||
if (updatableGuest) {
|
||||
updatableGuest.nick = guestParams.newNick;
|
||||
await user!.save();
|
||||
return updatableGuest;
|
||||
}
|
||||
else {
|
||||
throw new ModelParameterError("Guest with ID " + guestParams.id + " doesn't exist!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
DbUserSchema.methods.deleteGuest = async function (this: IDbUser, guestId: string): Promise<IPlayer> {
|
||||
return tryQuery(async () => {
|
||||
const user = await DbUser.findById(this.id);
|
||||
const deleteGuestIndex = user!.guests.findIndex(guest => guest.id === guestId);
|
||||
if (deleteGuestIndex !== -1) {
|
||||
const deletedGuest = user!.guests[deleteGuestIndex];
|
||||
user!.guests[deleteGuestIndex].remove();
|
||||
await user!.save();
|
||||
return deletedGuest;
|
||||
}
|
||||
else {
|
||||
throw new ModelParameterError("Guest with ID " + guestId + " doesn't exist!");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
DbUserSchema.methods.addGame = async function (submission: GameSubmission): Promise<ISavedGame> {
|
||||
const newGame = await SavedGame.createFromGameSubmission(submission);
|
||||
await tryQuery(() => {
|
||||
DbUser.findByIdAndUpdate(this.id, {$push: {savedGames: newGame}}).exec();
|
||||
});
|
||||
return newGame;
|
||||
};
|
||||
|
||||
DbUserSchema.methods.changeLang = async function (lang: SupportedLang): Promise<void> {
|
||||
if (lang in SupportedLang) {
|
||||
await tryQuery(() =>
|
||||
DbUser.findByIdAndUpdate(this.id, {lang: lang})
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw new ModelParameterError(lang + " is not a supported language code!");
|
||||
}
|
||||
};
|
||||
|
||||
DbUserSchema.methods.getMainPlayerInfo = async function (): Promise<IPlayer> {
|
||||
const user = await tryQuery(() =>
|
||||
DbUser.findById(this.id, {"player.nick": 1, "player._id": 1}).exec()
|
||||
);
|
||||
return user.player;
|
||||
};
|
||||
|
||||
const DbUser = mongoose.model<IDbUserDoc, IDbUserModel>("DbUser", DbUserSchema);
|
||||
export default DbUser;
|
||||
@@ -1,45 +1,20 @@
|
||||
import mongoose, {Types} from "mongoose";
|
||||
import Player, {IPlayer} from "./player";
|
||||
import Player, {IPlayer} from "./StoredPlayer";
|
||||
import {GameSubmission} from "../controllers/statsController";
|
||||
import {tryQuery, globalSchemaOptions} from "./utils";
|
||||
import DbUser from "./dbUser";
|
||||
import {tryQuery, globalSchemaOptions, StoredObjectCollection, StoredObject} from "./utils";
|
||||
import DbUser from "./dbUser_old";
|
||||
import {Ruleset} from "../rulesets";
|
||||
|
||||
export interface ISavedGame {
|
||||
export interface SavedGameData {
|
||||
id: string;
|
||||
//rulesetUsed?: ruleset;
|
||||
rulesetUsed: RulesetData;
|
||||
players: mongoose.Types.ObjectId[];
|
||||
results: any[];
|
||||
results: [];
|
||||
}
|
||||
|
||||
export interface ISavedGameDoc extends mongoose.Document {
|
||||
//rulesetUsed: mongoose.Types.ObjectId[];
|
||||
id: string;
|
||||
players: mongoose.Types.Array<mongoose.Types.ObjectId>;
|
||||
results: mongoose.Types.Array<mongoose.Types.Subdocument>;
|
||||
export interface StoredSavedGame extends StoredObject {
|
||||
}
|
||||
|
||||
export interface ISavedGameModel extends mongoose.Model<ISavedGameDoc> {
|
||||
// virtual static methods
|
||||
createFromGameSubmission(submission: GameSubmission): Promise<ISavedGameDoc>;
|
||||
export interface StoredSavedGameCollection extends StoredObjectCollection<StoredSavedGame> {
|
||||
createFromGameSubmission(submission: GameSubmission): Promise<StoredSavedGame>;
|
||||
}
|
||||
|
||||
export const SavedGameSchema = new mongoose.Schema({
|
||||
//rulesetUsed: [mongoose.Schema.Types.ObjectId],
|
||||
players: [mongoose.Schema.Types.ObjectId],
|
||||
results: [mongoose.Schema.Types.Mixed],
|
||||
}, {
|
||||
timestamps: true,
|
||||
...globalSchemaOptions
|
||||
});
|
||||
|
||||
SavedGameSchema.statics.createFromGameSubmission = async function(submission: GameSubmission) {
|
||||
const newGame = new SavedGame();
|
||||
newGame.results.addToSet(...submission.results);
|
||||
await tryQuery(async () => {
|
||||
newGame.players.addToSet(...submission.players.map(player => player.id));
|
||||
});
|
||||
return newGame;
|
||||
};
|
||||
|
||||
const SavedGame = mongoose.model<ISavedGameDoc, ISavedGameModel>("SavedGame", SavedGameSchema);
|
||||
export default SavedGame;
|
||||
@@ -18,26 +18,55 @@ export function getMongoObjectCollection(collectionName: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export type StoredObjectId = string;
|
||||
|
||||
export interface StoredObjectCollection<K> {
|
||||
findById(id: string): Promise<K>;
|
||||
findById(id: string): Promise<K | null>;
|
||||
}
|
||||
|
||||
export abstract class MongoStoredObjectCollection<T extends MongoStoredObject<any>, K extends StoredObject> implements StoredObjectCollection<K> {
|
||||
export abstract class MongoStoredObjectCollection<D, K extends StoredObject> implements StoredObjectCollection<K> {
|
||||
protected mongoDbClientCollection: mongo.Collection;
|
||||
protected constructor(collectionClient: mongo.Collection) {
|
||||
protected MongoStoredObject: new(data: D, ...args: any[]) => K;
|
||||
protected constructor(collectionClient: mongo.Collection, objectConstructor: new (data: D, ...args: any[]) => K) {
|
||||
this.mongoDbClientCollection = collectionClient;
|
||||
this.MongoStoredObject = objectConstructor;
|
||||
}
|
||||
|
||||
protected async findObjectById(id: string): Promise<any | null> {
|
||||
return this.mongoDbClientCollection!.findOne({_id: id});
|
||||
return tryQuery(async () =>
|
||||
await this.mongoDbClientCollection.findOne({_id: id})
|
||||
);
|
||||
}
|
||||
|
||||
abstract async findById(id: string): Promise<K>;
|
||||
protected async findObjectByAttribute(attribute: string, value: any): Promise<any | null> {
|
||||
return tryQuery(async () =>
|
||||
await this.mongoDbClientCollection.findOne({attribute: value})
|
||||
);
|
||||
}
|
||||
|
||||
async save(...objects: T[]): Promise<void> {
|
||||
protected async create(objectData: Partial<D>): Promise<K> {
|
||||
return tryQuery(async () =>
|
||||
await this.mongoDbClientCollection.insertOne(objectData)
|
||||
);
|
||||
}
|
||||
|
||||
async deleteById(objectId: StoredObjectId): Promise<K | null> {
|
||||
const deletedObject = this.findById(objectId);
|
||||
await this.mongoDbClientCollection.deleteOne({_id: objectId});
|
||||
return deletedObject;
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<K | null> {
|
||||
const data = await this.findObjectById(id);
|
||||
return new this.MongoStoredObject(data);
|
||||
};
|
||||
|
||||
async save(...objects: K[]): Promise<void> {
|
||||
await tryQuery(async () => {
|
||||
for (const object of objects) {
|
||||
await this.mongoDbClientCollection.findOneAndUpdate({_id: object.id()}, {...object.rawData()});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import passport from "passport";
|
||||
import {Strategy as LocalStrategy, VerifyFunction} from "passport-local";
|
||||
import bcrypt from "bcrypt";
|
||||
import DbUser, {IDbUser} from "./models/dbUser";
|
||||
import DbUser, {IDbUser} from "./models/dbUser_old";
|
||||
import {NextFunction, Request, Response} from "express";
|
||||
|
||||
export const requireAuthenticated = (req: Request, res: Response, next: NextFunction) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import express from "express";
|
||||
import {requireAuthenticated} from "../passport-config";
|
||||
import routers from "./routers";
|
||||
import {IDbUser} from "../models/dbUser";
|
||||
import {IDbUser} from "../models/dbUser_old";
|
||||
import {ModelParameterError} from "../models/utils";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
Reference in New Issue
Block a user