first commit to new repo
This commit is contained in:
14
src/controllers/dbUserController.ts
Executable file
14
src/controllers/dbUserController.ts
Executable file
@@ -0,0 +1,14 @@
|
||||
import passport from "passport";
|
||||
import DbUser, {IDbUser, IDbUserDoc} from "../models/dbUser";
|
||||
import {RequestHandler} from "express";
|
||||
import SavedGame from "../models/savedGame";
|
||||
|
||||
export const whoAmI: RequestHandler = async (req, res) => {
|
||||
if (req.isAuthenticated()) {
|
||||
const user = req.user as IDbUserDoc;
|
||||
res.json({loggedIn: true, username: user.username});
|
||||
}
|
||||
else {
|
||||
res.json({loggedIn: false});
|
||||
}
|
||||
};
|
||||
50
src/controllers/signupController.ts
Executable file
50
src/controllers/signupController.ts
Executable file
@@ -0,0 +1,50 @@
|
||||
import passport from "passport";
|
||||
import DbUser from "../models/dbUser";
|
||||
import {RequestHandler} from "express";
|
||||
|
||||
export const showLoginPage: RequestHandler = (req, res) => {
|
||||
res.render("login.ejs");
|
||||
};
|
||||
|
||||
export const loginUser: RequestHandler = (req, res) => {
|
||||
passport.authenticate('local', {
|
||||
successRedirect: req.app.locals.rootUrl + "/",
|
||||
failureRedirect: req.baseUrl + "/login",
|
||||
failureFlash: true,
|
||||
})(req, res);
|
||||
};
|
||||
|
||||
export const showRegistrationPage: RequestHandler = (req, res) => {
|
||||
res.render("register.ejs");
|
||||
};
|
||||
|
||||
export const registerNewUser: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const newUser = await DbUser.registerUser(req.body.username, req.body.email, req.body.password);
|
||||
req.login(newUser, (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
else {
|
||||
res.redirect(req.baseUrl + "/");
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
const errors: string[] = [];
|
||||
if (error.name === "CredentialsTakenError") {
|
||||
error.usernameExists && errors.push("That username is already taken");
|
||||
error.emailExists && errors.push("That email address is already taken");
|
||||
}
|
||||
else {
|
||||
errors.push("An error occurred during the registration.");
|
||||
}
|
||||
req.flash("errors", errors);
|
||||
res.render("register.ejs");
|
||||
}
|
||||
};
|
||||
|
||||
export const logoutUser: RequestHandler = (req, res) => {
|
||||
req.logout();
|
||||
res.redirect(req.baseUrl + "/login");
|
||||
};
|
||||
40
src/controllers/statsController.ts
Executable file
40
src/controllers/statsController.ts
Executable file
@@ -0,0 +1,40 @@
|
||||
import passport from "passport";
|
||||
import DbUser, {IDbUser, IDbUserDoc} from "../models/dbUser";
|
||||
import {RequestHandler} from "express";
|
||||
import SavedGame from "../models/savedGame";
|
||||
|
||||
export const listGames: RequestHandler = async (req, res) => {
|
||||
const user = (req.user as IDbUserDoc);
|
||||
const dbUser = await DbUser.findById(user._id, {"savedGames.game": 1, "savedGames.createdAt": 1});
|
||||
if (dbUser) {
|
||||
res.json({games: dbUser.savedGames});
|
||||
}
|
||||
else {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
};
|
||||
|
||||
export const saveGame: RequestHandler = async (req, res) => {
|
||||
const user = (req.user as IDbUserDoc);
|
||||
const handleError = (err: string) => {
|
||||
res.send({error: true, message: err});
|
||||
};
|
||||
DbUser.findById(user, (err, user) => {
|
||||
if (err) {
|
||||
handleError(err);
|
||||
}
|
||||
else if (user) {
|
||||
const newGame = new SavedGame();
|
||||
newGame.game = req.body;
|
||||
user.savedGames.push(newGame);
|
||||
user.save((err) => {
|
||||
if (err) {
|
||||
return handleError(err);
|
||||
}
|
||||
else {
|
||||
res.send("Thanks for submitting your game, " + user.username + "!");
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
47
src/index.ts
Executable file
47
src/index.ts
Executable file
@@ -0,0 +1,47 @@
|
||||
import express from "express";
|
||||
import {initialisePassport, requireAuthenticated, requireNotAuthenticated} from "./passport-config";
|
||||
import mongoose from "mongoose";
|
||||
import Settings from "./server-config.json";
|
||||
import flash from "express-flash";
|
||||
import passport from "passport";
|
||||
import session from "express-session";
|
||||
import * as path from "path";
|
||||
import MainRouter from "./routers/mainRouter";
|
||||
|
||||
// MongoDB Setup
|
||||
mongoose.connect(Settings.mongodb_uri, (err: any) => {
|
||||
if (err) {
|
||||
console.log(err.message);
|
||||
}
|
||||
else {
|
||||
console.log("Successfully connected to mongoDB!");
|
||||
}
|
||||
});
|
||||
|
||||
// Express app config
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.set("port", process.env.PORT || 3000);
|
||||
app.set("view-engine", "ejs");
|
||||
app.use(express.urlencoded({ extended: false}));
|
||||
app.use(flash());
|
||||
app.use(session({
|
||||
// TODO hide the secret
|
||||
secret: "secret",
|
||||
saveUninitialized: false,
|
||||
resave: false
|
||||
}));
|
||||
app.locals = {
|
||||
rootUrl: Settings.serverRoot
|
||||
};
|
||||
|
||||
// Passport init
|
||||
initialisePassport();
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
app.use(Settings.serverRoot, MainRouter);
|
||||
|
||||
const server = app.listen(app.get("port"), () => {
|
||||
console.log("App is running on http://localhost:%d", app.get("port"));
|
||||
});
|
||||
94
src/models/dbUser.ts
Executable file
94
src/models/dbUser.ts
Executable file
@@ -0,0 +1,94 @@
|
||||
import mongoose from "mongoose";
|
||||
import Player, {IPlayer, IPlayerDoc, PlayerSchema} from "./player";
|
||||
import {AccountStats, AccountStatsSchema, IAccountStats, IAccountStatsDoc} from "./stats";
|
||||
import {ISavedGame, ISavedGameDoc, SavedGameSchema} from "./savedGame";
|
||||
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 IDbUser {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
friends?: IDbUser[];
|
||||
player?: IPlayer;
|
||||
guests?: IPlayer[];
|
||||
accountStats?: IAccountStats;
|
||||
savedGames?: ISavedGame[];
|
||||
}
|
||||
|
||||
export interface IDbUserDoc extends mongoose.Document {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
friends: IDbUserDoc[];
|
||||
player: IPlayerDoc;
|
||||
guests: IPlayerDoc[];
|
||||
accountStats: IAccountStatsDoc;
|
||||
savedGames: ISavedGameDoc[];
|
||||
}
|
||||
|
||||
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): boolean;
|
||||
userWithUsernameExists(username: string): boolean;
|
||||
}
|
||||
|
||||
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 },
|
||||
friends: {type: [mongoose.Schema.Types.ObjectId], required: true, default: []},
|
||||
player: {type: PlayerSchema, required: true, unique: true},
|
||||
guests: {type: [PlayerSchema], required: true, default: []},
|
||||
accountStats: {type: AccountStatsSchema, required: true, default: () => ({}) },
|
||||
savedGames: {type: [SavedGameSchema], required: true, default: []},
|
||||
});
|
||||
|
||||
DbUserSchema.statics.findByEmail = async function (emailQuery: string) {
|
||||
return this.findOne({email: emailQuery});
|
||||
};
|
||||
|
||||
DbUserSchema.statics.addNewUser = async function (user: IDbUser) {
|
||||
const player = new Player( { nick: user.username });
|
||||
return this.create({
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
password: user.password,
|
||||
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 this.addNewUser({username, email, password: hashedPassword});
|
||||
}
|
||||
};
|
||||
|
||||
DbUserSchema.statics.userWithEmailExists = async function (email: string) {
|
||||
return this.exists({email});
|
||||
};
|
||||
|
||||
DbUserSchema.statics.userWithUsernameExists = async function (username: string) {
|
||||
return this.exists({username});
|
||||
};
|
||||
|
||||
const DbUser = mongoose.model<IDbUserDoc, IDbUserModel>("DbUser", DbUserSchema);
|
||||
export default DbUser;
|
||||
24
src/models/player.ts
Executable file
24
src/models/player.ts
Executable file
@@ -0,0 +1,24 @@
|
||||
import mongoose from "mongoose";
|
||||
import {IPlayerStats, IPlayerStatsDoc, PlayerStatsSchema} from "./stats";
|
||||
|
||||
export interface IPlayer {
|
||||
nick: string;
|
||||
stats: IPlayerStats;
|
||||
}
|
||||
|
||||
export interface IPlayerDoc extends mongoose.Document {
|
||||
nick: string;
|
||||
stats: IPlayerStats;
|
||||
}
|
||||
|
||||
export interface IPlayerModel extends mongoose.Model<IPlayerDoc> {
|
||||
// virtual static methods
|
||||
}
|
||||
|
||||
export const PlayerSchema = new mongoose.Schema({
|
||||
nick: { type: String, required: true },
|
||||
stats: { type: PlayerStatsSchema, required: true, default: () => ({}) },
|
||||
});
|
||||
|
||||
const Player = mongoose.model<IPlayerDoc, IPlayerModel>("Player", PlayerSchema);
|
||||
export default Player;
|
||||
0
src/models/ruleset.ts
Executable file
0
src/models/ruleset.ts
Executable file
24
src/models/savedGame.ts
Executable file
24
src/models/savedGame.ts
Executable file
@@ -0,0 +1,24 @@
|
||||
import mongoose from "mongoose";
|
||||
import {IPlayer} from "./player";
|
||||
|
||||
export interface ISavedGame {
|
||||
//players: IPlayer[],
|
||||
game: any,
|
||||
}
|
||||
|
||||
export interface ISavedGameDoc extends mongoose.Document {
|
||||
//players: mongoose.Types.ObjectId[],
|
||||
game: mongoose.Types.Subdocument,
|
||||
}
|
||||
|
||||
export interface ISavedGameModel extends mongoose.Model<ISavedGameDoc> {
|
||||
// virtual static methods
|
||||
}
|
||||
|
||||
export const SavedGameSchema = new mongoose.Schema({
|
||||
//players: [mongoose.Schema.Types.ObjectId],
|
||||
game: mongoose.Schema.Types.Mixed,
|
||||
}, {timestamps: true});
|
||||
|
||||
const SavedGame = mongoose.model<ISavedGameDoc, ISavedGameModel>("SavedGame", SavedGameSchema);
|
||||
export default SavedGame;
|
||||
205
src/models/stats.ts
Executable file
205
src/models/stats.ts
Executable file
@@ -0,0 +1,205 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
// Interfaces and types for business logic
|
||||
export interface IPlayerStats extends IBaseStats {
|
||||
wins: number;
|
||||
runnerUps: number;
|
||||
draws: number;
|
||||
losses: number;
|
||||
}
|
||||
export interface IAccountStats extends IBaseStats {
|
||||
timesNoWinner: number;
|
||||
}
|
||||
export interface IBaseStats {
|
||||
one: IMultiplierFieldStats;
|
||||
two: IMultiplierFieldStats;
|
||||
three: IMultiplierFieldStats;
|
||||
four: IMultiplierFieldStats;
|
||||
five: IMultiplierFieldStats;
|
||||
six: IMultiplierFieldStats;
|
||||
upperBonus: IBonusFieldStats;
|
||||
upperTotal: ITotalFieldStats;
|
||||
threeKind: INumberFieldStats;
|
||||
fourKind: INumberFieldStats;
|
||||
fullHouse: IBoolFieldStats;
|
||||
smlStraight: IBoolFieldStats;
|
||||
lgStraight: IBoolFieldStats;
|
||||
yahtzee: IYahtzeeFieldStats;
|
||||
chance: INumberFieldStats;
|
||||
grandTotal: ITotalFieldStats;
|
||||
lowerTotal: ITotalFieldStats;
|
||||
gamesPlayed: number;
|
||||
}
|
||||
interface IBonusFieldStats {
|
||||
total: number;
|
||||
}
|
||||
interface ITotalFieldStats {
|
||||
average: number;
|
||||
best: number;
|
||||
worst: number;
|
||||
}
|
||||
interface IBoolFieldStats {
|
||||
average: number;
|
||||
timesStruck: number;
|
||||
total: number;
|
||||
}
|
||||
interface INumberFieldStats {
|
||||
average: number;
|
||||
timesStruck: number;
|
||||
best: number;
|
||||
worst: number;
|
||||
}
|
||||
type IMultiplierFieldStats = INumberFieldStats;
|
||||
type IYahtzeeFieldStats = INumberFieldStats;
|
||||
|
||||
// Mongoose doc interfaces and types
|
||||
export interface IPlayerStats extends IBaseStats {
|
||||
one: IMultiplierFieldStatsDoc;
|
||||
two: IMultiplierFieldStatsDoc;
|
||||
three: IMultiplierFieldStatsDoc;
|
||||
four: IMultiplierFieldStatsDoc;
|
||||
five: IMultiplierFieldStatsDoc;
|
||||
six: IMultiplierFieldStatsDoc;
|
||||
upperBonus: IBonusFieldStatsDoc;
|
||||
upperTotal: ITotalFieldStatsDoc;
|
||||
threeKind: INumberFieldStatsDoc;
|
||||
fourKind: INumberFieldStatsDoc;
|
||||
fullHouse: IBoolFieldStatsDoc;
|
||||
smlStraight: IBoolFieldStatsDoc;
|
||||
lgStraight: IBoolFieldStatsDoc;
|
||||
yahtzee: IYahtzeeFieldStatsDoc;
|
||||
chance: INumberFieldStatsDoc;
|
||||
grandTotal: ITotalFieldStatsDoc;
|
||||
lowerTotal: ITotalFieldStatsDoc;
|
||||
gamesPlayed: number;
|
||||
wins: number;
|
||||
runnerUps: number;
|
||||
draws: number;
|
||||
losses: number;
|
||||
}
|
||||
export interface IAccountStats extends IBaseStats {
|
||||
one: IMultiplierFieldStatsDoc;
|
||||
two: IMultiplierFieldStatsDoc;
|
||||
three: IMultiplierFieldStatsDoc;
|
||||
four: IMultiplierFieldStatsDoc;
|
||||
five: IMultiplierFieldStatsDoc;
|
||||
six: IMultiplierFieldStatsDoc;
|
||||
upperBonus: IBonusFieldStatsDoc;
|
||||
upperTotal: ITotalFieldStatsDoc;
|
||||
threeKind: INumberFieldStatsDoc;
|
||||
fourKind: INumberFieldStatsDoc;
|
||||
fullHouse: IBoolFieldStatsDoc;
|
||||
smlStraight: IBoolFieldStatsDoc;
|
||||
lgStraight: IBoolFieldStatsDoc;
|
||||
yahtzee: IYahtzeeFieldStatsDoc;
|
||||
chance: INumberFieldStatsDoc;
|
||||
grandTotal: ITotalFieldStatsDoc;
|
||||
lowerTotal: ITotalFieldStatsDoc;
|
||||
gamesPlayed: number;
|
||||
timesNoWinner: number;
|
||||
}
|
||||
type IBonusFieldStatsDoc = mongoose.Document & IBonusFieldStats;
|
||||
type ITotalFieldStatsDoc = mongoose.Document & ITotalFieldStats;
|
||||
type IBoolFieldStatsDoc = mongoose.Document & IBoolFieldStats;
|
||||
type INumberFieldStatsDoc = mongoose.Document & INumberFieldStats;
|
||||
type IMultiplierFieldStatsDoc = mongoose.Document & IMultiplierFieldStats;
|
||||
type IYahtzeeFieldStatsDoc = mongoose.Document & IYahtzeeFieldStats;
|
||||
export type IPlayerStatsDoc = mongoose.Document & IPlayerStats;
|
||||
export type IAccountStatsDoc = mongoose.Document & IAccountStats;
|
||||
|
||||
// Mongoose schemata
|
||||
class Int extends mongoose.SchemaType {
|
||||
constructor(key: string, options: any) {
|
||||
super(key, options, 'Int');
|
||||
}
|
||||
cast(val: any): number {
|
||||
let _val = Number(val);
|
||||
if (isNaN(_val)) {
|
||||
throw new Error('ZeroPositiveInt: ' + val + ' is not a number');
|
||||
}
|
||||
_val = Math.round(_val);
|
||||
return _val;
|
||||
}
|
||||
}
|
||||
(mongoose.Schema.Types as any).Int = Int;
|
||||
|
||||
const BonusFieldStatsSchema = new mongoose.Schema( {
|
||||
average: {type: Number, required: true, default: 0, min: 0},
|
||||
total: {type: Int, required: true, default: 0, min: 0}
|
||||
}, { _id: false });
|
||||
const TotalFieldStatsSchema = new mongoose.Schema( {
|
||||
average: {type: Number, required: true, default: 0, min: 0},
|
||||
best: {type: Int, required: true, default: 0, min: 0},
|
||||
worst: {type: Int, required: true, default: 0, min: 0},
|
||||
}, { _id: false });
|
||||
const BoolFieldStatsSchema = new mongoose.Schema( {
|
||||
average: {type: Number, required: true, default: 0, min: 0, max: 1},
|
||||
timesStruck: {type: Int, required: true, default: 0, min: 0},
|
||||
total: {type: Int, required: true, default: 0, min: 0},
|
||||
}, { _id: false });
|
||||
const NumberFieldStatsSchema = new mongoose.Schema( {
|
||||
average: {type: Number, required: true, default: 0, min: 0},
|
||||
timesStruck: {type: Int, required: true, default: 0, min: 0},
|
||||
best: {type: Int, required: true, default: 0, min: 0},
|
||||
worst: {type: Int, required: true, default: 0, min: 0},
|
||||
}, { _id: false });
|
||||
const MultiplierFieldStatsSchema = new mongoose.Schema( {
|
||||
average: {type: Number, required: true, default: 0, min: 0},
|
||||
timesStruck: {type: Int, required: true, default: 0, min: 0},
|
||||
best: {type: Int, required: true, default: 0, min: 0},
|
||||
worst: {type: Int, required: true, default: 0, min: 0},
|
||||
}, { _id: false });
|
||||
const YahtzeeFieldStatsSchema = new mongoose.Schema( {
|
||||
average: {type: Number, required: true, default: 0, min: 0},
|
||||
timesStruck: {type: Int, required: true, default: 0, min: 0},
|
||||
best: {type: Int, required: true, default: 0, min: 0},
|
||||
worst: {type: Int, required: true, default: 0, min: 0},
|
||||
}, { _id: false });
|
||||
export const PlayerStatsSchema = new mongoose.Schema( {
|
||||
one: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
two: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
three: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
four: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
five: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
six: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
upperBonus: { type: BonusFieldStatsSchema, required: true, default: () => ({}) },
|
||||
upperTotal: { type: TotalFieldStatsSchema, required: true, default: () => ({}) },
|
||||
threeKind: { type: NumberFieldStatsSchema, required: true, default: () => ({}) },
|
||||
fourKind: { type: NumberFieldStatsSchema, required: true, default: () => ({}) },
|
||||
fullHouse: { type: BoolFieldStatsSchema, required: true, default: () => ({}) },
|
||||
smlStraight: { type: BoolFieldStatsSchema, required: true, default: () => ({}) },
|
||||
lgStraight: { type: BoolFieldStatsSchema, required: true, default: () => ({}) },
|
||||
yahtzee: { type: YahtzeeFieldStatsSchema, required: true, default: () => ({}) },
|
||||
chance: { type: NumberFieldStatsSchema, required: true, default: () => ({}) },
|
||||
grandTotal: { type: TotalFieldStatsSchema, required: true, default: () => ({}) },
|
||||
lowerTotal: { type: TotalFieldStatsSchema, required: true, default: () => ({}) },
|
||||
gamesPlayed: { type: Int, required: true, default: 0, min: 0 },
|
||||
wins: { type: Int, required: true, default: 0, min: 0 },
|
||||
runnerUps: { type: Int, required: true, default: 0, min: 0 },
|
||||
draws: { type: Int, required: true, default: 0, min: 0 },
|
||||
losses: { type: Int, required: true, default: 0, min: 0 },
|
||||
}, { _id: false });
|
||||
export const AccountStatsSchema = new mongoose.Schema( {
|
||||
one: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
two: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
three: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
four: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
five: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
six: { type: MultiplierFieldStatsSchema, required: true, default: () => ({}) },
|
||||
upperBonus: { type: BonusFieldStatsSchema, required: true, default: () => ({}) },
|
||||
upperTotal: { type: TotalFieldStatsSchema, required: true, default: () => ({}) },
|
||||
threeKind: { type: NumberFieldStatsSchema, required: true, default: () => ({}) },
|
||||
fourKind: { type: NumberFieldStatsSchema, required: true, default: () => ({}) },
|
||||
fullHouse: { type: BoolFieldStatsSchema, required: true, default: () => ({}) },
|
||||
smlStraight: { type: BoolFieldStatsSchema, required: true, default: () => ({}) },
|
||||
lgStraight: { type: BoolFieldStatsSchema, required: true, default: () => ({}) },
|
||||
yahtzee: { type: YahtzeeFieldStatsSchema, required: true, default: () => ({}) },
|
||||
chance: { type: NumberFieldStatsSchema, required: true, default: () => ({}) },
|
||||
grandTotal: { type: TotalFieldStatsSchema, required: true, default: () => ({}) },
|
||||
lowerTotal: { type: TotalFieldStatsSchema, required: true, default: () => ({}) },
|
||||
gamesPlayed: { type: Int, required: true, default: 0, min: 0 },
|
||||
timesNoWinner: { type: Int, required: true, default: 0, min: 0 },
|
||||
}, { _id: false });
|
||||
|
||||
export const PlayerStats = mongoose.model<IPlayerStatsDoc>("PlayerStats", PlayerStatsSchema);
|
||||
export const AccountStats = mongoose.model<IAccountStatsDoc>("AccountStats", AccountStatsSchema);
|
||||
51
src/passport-config.ts
Executable file
51
src/passport-config.ts
Executable file
@@ -0,0 +1,51 @@
|
||||
import passport from "passport";
|
||||
import {Strategy as LocalStrategy, VerifyFunction} from "passport-local";
|
||||
import bcrypt from "bcrypt";
|
||||
import DbUser, {IDbUserDoc} from "./models/dbUser";
|
||||
import {NextFunction, Request, Response} from "express";
|
||||
|
||||
export const requireAuthenticated = (req: Request, res: Response, next: NextFunction) => {
|
||||
if (req.isAuthenticated()) {
|
||||
return next();
|
||||
}
|
||||
else {
|
||||
res.redirect(req.baseUrl + "/account/login");
|
||||
}
|
||||
};
|
||||
|
||||
export const requireNotAuthenticated = (req: Request, res: Response, next: NextFunction) => {
|
||||
if (req.isAuthenticated()) {
|
||||
res.redirect(req.app.locals.rootUrl + "/");
|
||||
}
|
||||
else {
|
||||
return next();
|
||||
}
|
||||
};
|
||||
|
||||
const authenticateUser: VerifyFunction = async (email, password, done) => {
|
||||
const user = await DbUser.findByEmail(email);
|
||||
if (!user) {
|
||||
return done(null, false, { message: "A user with that email does not exist."} );
|
||||
}
|
||||
try {
|
||||
if (await bcrypt.compare(password, user.password)) {
|
||||
return done(null, user);
|
||||
} else {
|
||||
return done(null, false, {message: "Password incorrect"});
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
return done(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const initialisePassport = () => {
|
||||
passport.use(new LocalStrategy({ usernameField: "email" }, authenticateUser));
|
||||
passport.serializeUser((user: IDbUserDoc, done) => {
|
||||
done(null, user._id)
|
||||
});
|
||||
passport.deserializeUser(async (id: string, done) => {
|
||||
const user: IDbUserDoc | null = await DbUser.findById(id);
|
||||
done(null, user);
|
||||
});
|
||||
};
|
||||
12
src/routers/apiRouter.ts
Executable file
12
src/routers/apiRouter.ts
Executable file
@@ -0,0 +1,12 @@
|
||||
import express from "express";
|
||||
import {requireAuthenticated, requireNotAuthenticated} from "../passport-config";
|
||||
import * as stats from "../controllers/statsController";
|
||||
import * as dbUser from "../controllers/dbUserController"
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/user", dbUser.whoAmI);
|
||||
router.get("/games", requireAuthenticated, stats.listGames);
|
||||
router.post("/savegame", requireAuthenticated, stats.saveGame);
|
||||
|
||||
export default router;
|
||||
23
src/routers/mainRouter.ts
Executable file
23
src/routers/mainRouter.ts
Executable file
@@ -0,0 +1,23 @@
|
||||
import express from "express";
|
||||
import {requireAuthenticated, requireNotAuthenticated} from "../passport-config";
|
||||
import routers from "./routers";
|
||||
import {IDbUser} from "../models/dbUser";
|
||||
import * as path from "path";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.use("/account", routers.signup);
|
||||
router.use("/api", routers.api);
|
||||
router.use("/game/static", express.static(path.join(__dirname, "../game/static")));
|
||||
router.use("/static", express.static(path.join(__dirname, "../frontend/static")));
|
||||
router.use("/static", express.static("static"));
|
||||
|
||||
// General Endpoints
|
||||
router.get("/game", requireAuthenticated, (req, res) => {
|
||||
res.sendFile(path.join(__dirname + "/../game/index.html"));
|
||||
});
|
||||
router.get("", requireAuthenticated, (req, res) => {
|
||||
res.sendFile(path.join(__dirname + "/../frontend/index.html"), {username: (req.user as IDbUser).username});
|
||||
});
|
||||
|
||||
export default router;
|
||||
9
src/routers/routers.ts
Executable file
9
src/routers/routers.ts
Executable file
@@ -0,0 +1,9 @@
|
||||
import signupRouter from "./signupRouter";
|
||||
import apiRouter from "./apiRouter";
|
||||
|
||||
const routers = {
|
||||
signup: signupRouter,
|
||||
api: apiRouter,
|
||||
};
|
||||
|
||||
export default routers;
|
||||
17
src/routers/signupRouter.ts
Executable file
17
src/routers/signupRouter.ts
Executable file
@@ -0,0 +1,17 @@
|
||||
import express from "express";
|
||||
import {requireAuthenticated, requireNotAuthenticated} from "../passport-config";
|
||||
import * as signup from "../controllers/signupController";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/login", requireNotAuthenticated, signup.showLoginPage);
|
||||
|
||||
router.post("/login", requireNotAuthenticated, signup.loginUser);
|
||||
|
||||
router.get("/register", requireNotAuthenticated, signup.showRegistrationPage);
|
||||
|
||||
router.post("/register", requireNotAuthenticated, signup.registerNewUser);
|
||||
|
||||
router.get("/logout", requireAuthenticated, signup.logoutUser);
|
||||
|
||||
export default router;
|
||||
4
src/server-config.json
Executable file
4
src/server-config.json
Executable file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"mongodb_uri": "mongodb://127.0.0.1:27017/test",
|
||||
"serverRoot": "/kadi"
|
||||
}
|
||||
Reference in New Issue
Block a user