Files
kadi_backend/src/models/StoredUser.ts

215 lines
7.3 KiB
TypeScript

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;