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",
|
- 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
|
"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).
|
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 {RequestHandler} from "express";
|
||||||
import {IPlayer} from "../models/player";
|
import {IPlayer} from "../models/StoredPlayer";
|
||||||
|
|
||||||
export const whoAmI: RequestHandler = async (req, res) => {
|
export const whoAmI: RequestHandler = async (req, res) => {
|
||||||
if (req.isAuthenticated()) {
|
if (req.isAuthenticated()) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import passport from "passport";
|
import passport from "passport";
|
||||||
import DbUser from "../models/dbUser";
|
import DbUser from "../models/dbUser_old";
|
||||||
import {RequestHandler} from "express";
|
import {RequestHandler} from "express";
|
||||||
|
|
||||||
export const showLoginPage: RequestHandler = (req, res) => {
|
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 { RequestHandler } from "express";
|
||||||
import Player, { IPlayer } from "../models/player";
|
import Player, { IPlayer } from "../models/StoredPlayer";
|
||||||
|
|
||||||
const DEFAULT_RULESET = "DEFAULT_RULESET";
|
const DEFAULT_RULESET = "DEFAULT_RULESET";
|
||||||
const UPPER_BONUS_THRESHOLD = 63;
|
const UPPER_BONUS_THRESHOLD = 63;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ app.locals = {
|
|||||||
initialisePassport();
|
initialisePassport();
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
app.use(passport.session());
|
app.use(passport.session());
|
||||||
|
|
||||||
app.use(Settings.serverRoot + "/static", express.static("static"));
|
app.use(Settings.serverRoot + "/static", express.static("static"));
|
||||||
app.use(Settings.serverRoot, MainRouter);
|
app.use(Settings.serverRoot, MainRouter);
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from "./stats";
|
} from "./stats";
|
||||||
import {
|
import {
|
||||||
getMongoObjectCollection,
|
getMongoObjectCollection,
|
||||||
MongoStoredObject, MongoStoredObjectCollection, StoredObject, StoredObjectCollection,
|
MongoStoredObject, MongoStoredObjectCollection, StoredObject, StoredObjectCollection, tryQuery,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import {CellValue} from "../controllers/statsController";
|
import {CellValue} from "../controllers/statsController";
|
||||||
import mongo from "mongodb";
|
import mongo from "mongodb";
|
||||||
@@ -24,22 +24,22 @@ export interface StoredPlayerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface StoredPlayerCollection extends StoredObjectCollection<StoredPlayer> {
|
interface StoredPlayerCollection extends StoredObjectCollection<StoredPlayer> {
|
||||||
findById(id: string): Promise<StoredPlayer>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MongoStoredPlayerCollection
|
class MongoStoredPlayerCollection
|
||||||
extends MongoStoredObjectCollection<MongoStoredPlayer, StoredPlayer>
|
extends MongoStoredObjectCollection<StoredPlayerData, StoredPlayer>
|
||||||
implements StoredPlayerCollection {
|
implements StoredPlayerCollection {
|
||||||
|
|
||||||
private updater: PlayerStatsUpdater;
|
private updater: PlayerStatsUpdater;
|
||||||
constructor(collectionClient: mongo.Collection) {
|
constructor(collectionClient: mongo.Collection) {
|
||||||
super(collectionClient);
|
super(collectionClient, MongoStoredPlayer);
|
||||||
this.updater = new PlayerStatsUpdater();
|
this.updater = new PlayerStatsUpdater();
|
||||||
}
|
}
|
||||||
|
|
||||||
async findById(id: string): Promise<StoredPlayer> {
|
newPlayer(nick: string): Promise<StoredPlayer> {
|
||||||
const data = await this.findObjectById(id);
|
return tryQuery(async () => {
|
||||||
return new MongoStoredPlayer(data);
|
return this.create({nick});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export interface StoredPlayer extends StoredObject {
|
|||||||
updateStats(results: PlayerGameResults & {outcome: OutcomeType}, ruleset: Ruleset): Promise<void>;
|
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) {
|
constructor(data: StoredPlayerData) {
|
||||||
super(data);
|
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 mongoose, {Types} from "mongoose";
|
||||||
import Player, {IPlayer} from "./player";
|
import Player, {IPlayer} from "./StoredPlayer";
|
||||||
import {GameSubmission} from "../controllers/statsController";
|
import {GameSubmission} from "../controllers/statsController";
|
||||||
import {tryQuery, globalSchemaOptions} from "./utils";
|
import {tryQuery, globalSchemaOptions, StoredObjectCollection, StoredObject} from "./utils";
|
||||||
import DbUser from "./dbUser";
|
import DbUser from "./dbUser_old";
|
||||||
|
import {Ruleset} from "../rulesets";
|
||||||
|
|
||||||
export interface ISavedGame {
|
export interface SavedGameData {
|
||||||
id: string;
|
id: string;
|
||||||
//rulesetUsed?: ruleset;
|
rulesetUsed: RulesetData;
|
||||||
players: mongoose.Types.ObjectId[];
|
players: mongoose.Types.ObjectId[];
|
||||||
results: any[];
|
results: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISavedGameDoc extends mongoose.Document {
|
export interface StoredSavedGame extends StoredObject {
|
||||||
//rulesetUsed: mongoose.Types.ObjectId[];
|
|
||||||
id: string;
|
|
||||||
players: mongoose.Types.Array<mongoose.Types.ObjectId>;
|
|
||||||
results: mongoose.Types.Array<mongoose.Types.Subdocument>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISavedGameModel extends mongoose.Model<ISavedGameDoc> {
|
export interface StoredSavedGameCollection extends StoredObjectCollection<StoredSavedGame> {
|
||||||
// virtual static methods
|
createFromGameSubmission(submission: GameSubmission): Promise<StoredSavedGame>;
|
||||||
createFromGameSubmission(submission: GameSubmission): Promise<ISavedGameDoc>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
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 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.mongoDbClientCollection = collectionClient;
|
||||||
|
this.MongoStoredObject = objectConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async findObjectById(id: string): Promise<any | null> {
|
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> {
|
||||||
for (const object of objects) {
|
return tryQuery(async () =>
|
||||||
await this.mongoDbClientCollection.findOneAndUpdate({_id: object.id()}, {...object.rawData()});
|
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 passport from "passport";
|
||||||
import {Strategy as LocalStrategy, VerifyFunction} from "passport-local";
|
import {Strategy as LocalStrategy, VerifyFunction} from "passport-local";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import DbUser, {IDbUser} from "./models/dbUser";
|
import DbUser, {IDbUser} from "./models/dbUser_old";
|
||||||
import {NextFunction, Request, Response} from "express";
|
import {NextFunction, Request, Response} from "express";
|
||||||
|
|
||||||
export const requireAuthenticated = (req: Request, res: Response, next: NextFunction) => {
|
export const requireAuthenticated = (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import {requireAuthenticated} from "../passport-config";
|
import {requireAuthenticated} from "../passport-config";
|
||||||
import routers from "./routers";
|
import routers from "./routers";
|
||||||
import {IDbUser} from "../models/dbUser";
|
import {IDbUser} from "../models/dbUser_old";
|
||||||
import {ModelParameterError} from "../models/utils";
|
import {ModelParameterError} from "../models/utils";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|||||||
Reference in New Issue
Block a user