2018-09-23 20:17:35 +00:00
|
|
|
import * as express from "express";
|
|
|
|
import * as bodyparser from "body-parser";
|
|
|
|
|
|
|
|
import { authRoute } from "../security/token";
|
|
|
|
|
2018-09-29 13:06:14 +00:00
|
|
|
import { userFromSession } from "../utils/user";
|
|
|
|
|
2018-09-29 17:34:22 +00:00
|
|
|
import { IUser, IUserDBModel } from "../models/user";
|
2018-09-29 17:10:27 +00:00
|
|
|
import { LRequest } from "../types/express";
|
|
|
|
import { Db } from "mongodb";
|
2018-09-29 13:06:14 +00:00
|
|
|
|
2018-09-23 20:17:35 +00:00
|
|
|
const userRouter = express.Router();
|
|
|
|
userRouter.use(authRoute);
|
2018-09-29 12:23:09 +00:00
|
|
|
userRouter.use(bodyparser.json());
|
2018-09-23 20:17:35 +00:00
|
|
|
|
|
|
|
// Return the user object if the user is still authenticated
|
2018-09-29 17:10:27 +00:00
|
|
|
userRouter.get("/me", async (req: LRequest, res) => {
|
2018-09-29 12:23:09 +00:00
|
|
|
const { db, token } = req;
|
2018-09-23 20:17:35 +00:00
|
|
|
|
2018-09-29 12:23:09 +00:00
|
|
|
const session = await db.collection("sessions").findOne({ token, });
|
|
|
|
if (session !== null) {
|
|
|
|
const user = await db.collection("users").findOne({ username: session.username });
|
|
|
|
|
2018-09-29 13:06:14 +00:00
|
|
|
// Copy and strip unneeded attributes
|
|
|
|
let copy = Object.assign({}, user, {
|
|
|
|
sessionToken: token,
|
|
|
|
});
|
|
|
|
delete copy._id;
|
|
|
|
delete copy.hash;
|
|
|
|
delete copy.salt;
|
|
|
|
|
2018-09-29 12:23:09 +00:00
|
|
|
res.send({
|
|
|
|
error: "0",
|
2018-09-29 13:06:14 +00:00
|
|
|
data: copy,
|
2018-09-29 12:23:09 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
res.send({
|
|
|
|
error: "404",
|
|
|
|
data: {},
|
|
|
|
});
|
|
|
|
}
|
2018-09-23 20:17:35 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Removes the user's session
|
2018-09-29 17:10:27 +00:00
|
|
|
userRouter.get("/logout", async (req: LRequest, res) => {
|
2018-09-29 12:23:09 +00:00
|
|
|
// Try to remove the session
|
2018-09-29 17:10:27 +00:00
|
|
|
const { db, token } = req;
|
|
|
|
await db.collection("sessions").findOneAndDelete({ token, });
|
2018-09-23 20:17:35 +00:00
|
|
|
|
|
|
|
res.send({
|
|
|
|
error: "0",
|
|
|
|
data: {},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-09-24 11:53:20 +00:00
|
|
|
// TODO: This should be shared with the frontend, to remove code duplication
|
|
|
|
export enum VocabType {
|
|
|
|
NOMEN = 0,
|
|
|
|
VERB = 1,
|
|
|
|
ADJEKTIV = 2,
|
|
|
|
ADVERB = 3,
|
|
|
|
};
|
|
|
|
|
2018-09-23 20:17:35 +00:00
|
|
|
// Gets the user's review queue
|
2018-09-29 17:34:22 +00:00
|
|
|
userRouter.get("/queue", async (req: LRequest, res) => {
|
|
|
|
// TODO: if (user)
|
|
|
|
const { token, db } = req;
|
|
|
|
const user = await userFromSession(token, db);
|
|
|
|
|
|
|
|
// Fetch all vocab ids from the vocabulary collection
|
|
|
|
const vocabRaw = await db.collection("vocabulary").find({
|
|
|
|
id: { $in: user.queue },
|
|
|
|
}, {
|
|
|
|
// TODO: Make this configurable?
|
|
|
|
limit: 20,
|
|
|
|
}).toArray();
|
|
|
|
|
|
|
|
// Remove unneeded data
|
|
|
|
const vocab = vocabRaw.map(el => {
|
|
|
|
let tmp = Object.assign({}, el);
|
|
|
|
delete tmp._id;
|
|
|
|
|
|
|
|
return tmp;
|
|
|
|
});
|
2018-09-23 20:17:35 +00:00
|
|
|
|
|
|
|
res.send({
|
|
|
|
error: "0",
|
2018-09-24 11:53:20 +00:00
|
|
|
data: {
|
2018-09-29 17:34:22 +00:00
|
|
|
queue: vocab,
|
2018-09-24 11:53:20 +00:00
|
|
|
},
|
2018-09-23 20:17:35 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Get ot set the last review results
|
2018-09-29 17:10:27 +00:00
|
|
|
userRouter.get("/lastReview", async (req: LRequest, res) => {
|
|
|
|
// TODO: if(user)
|
|
|
|
const { token, db } = req;
|
|
|
|
const user = await userFromSession(token, db);
|
2018-09-23 20:17:35 +00:00
|
|
|
|
|
|
|
res.send({
|
|
|
|
error: "0",
|
2018-09-29 17:10:27 +00:00
|
|
|
data: user.lastReview,
|
2018-09-23 20:17:35 +00:00
|
|
|
});
|
|
|
|
});
|
2018-09-29 17:10:27 +00:00
|
|
|
userRouter.post("/lastReview", async (req: LRequest, res) => {
|
|
|
|
// TODO: Check if we get the correct data
|
|
|
|
// TODO: if(user)
|
|
|
|
const { token, db } = req;
|
|
|
|
const user = await userFromSession(token, db);
|
|
|
|
|
|
|
|
// TODO: Error handling
|
|
|
|
await db.collection("users").updateOne({
|
|
|
|
username: user.username,
|
|
|
|
}, {
|
|
|
|
$set: {
|
|
|
|
lastReview: req.body.meta,
|
|
|
|
},
|
|
|
|
});
|
2018-09-23 20:17:35 +00:00
|
|
|
|
|
|
|
res.send({
|
|
|
|
error: "0",
|
|
|
|
data: {},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-09-29 17:10:27 +00:00
|
|
|
// Returns the next level (level + 1) or the current level, if no higher level
|
|
|
|
// can be found
|
|
|
|
async function getNextLevel(token: string, db: Db): Promise<any> {
|
|
|
|
// TODO: if(user)
|
|
|
|
const user = await userFromSession(token, db);
|
|
|
|
const { lastLevel } = user;
|
2018-09-23 20:17:35 +00:00
|
|
|
|
2018-09-29 17:10:27 +00:00
|
|
|
// Try to find a level, which level is lastLevel + 1
|
|
|
|
const level = await db.collection("levels").findOne({
|
|
|
|
level: lastLevel + 1,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (level) {
|
|
|
|
return level;
|
|
|
|
} else {
|
|
|
|
// TODO: Send different data, so that the Client can say "Hey, no more levels"
|
|
|
|
return await db.collection("levels").findOne({
|
|
|
|
level: lastLevel
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the next level
|
|
|
|
userRouter.get("/nextLevel", async (req: LRequest, res) => {
|
|
|
|
const level = await getNextLevel(req.token, req.db);
|
2018-09-23 20:17:35 +00:00
|
|
|
res.send({
|
|
|
|
error: "0",
|
2018-09-29 17:10:27 +00:00
|
|
|
data: level,
|
2018-09-23 20:17:35 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Mark a level as done
|
2018-09-29 20:00:15 +00:00
|
|
|
userRouter.post("/level/:id", async (req: LRequest, res) => {
|
|
|
|
// Is everything specified?
|
|
|
|
if (!req.params || !req.params.id) {
|
|
|
|
res.send({
|
|
|
|
error: "400",
|
|
|
|
data: {
|
|
|
|
msg: "No level ID specified",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const id = parseInt(req.params.id);
|
|
|
|
if (id === NaN) {
|
|
|
|
res.send({
|
|
|
|
error: "400",
|
|
|
|
data: {
|
|
|
|
msg: "Invalid level ID",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: if (user)
|
|
|
|
const { token, db } = req;
|
|
|
|
const user = await userFromSession(token, db);
|
|
|
|
|
|
|
|
if (id in user.levels) {
|
|
|
|
// Nothing
|
|
|
|
} else {
|
|
|
|
// The level is new to the user
|
|
|
|
// Is the new level higher than the "highest" already completed level?
|
|
|
|
let update = {
|
|
|
|
levels: user.levels.concat(id),
|
|
|
|
};
|
|
|
|
if (id > Math.max(...user.levels)) {
|
2018-09-29 20:09:49 +00:00
|
|
|
// TODO: Add the levels vocabulary to the users review queue
|
2018-09-29 20:00:15 +00:00
|
|
|
// Also update the lastLevel attribute
|
|
|
|
Object.assign(update, { lastLevel: id });
|
|
|
|
}
|
|
|
|
await db.collection("users").updateOne({
|
|
|
|
username: user.username,
|
|
|
|
}, {
|
|
|
|
$set: update,
|
|
|
|
});
|
|
|
|
}
|
2018-09-23 20:17:35 +00:00
|
|
|
|
|
|
|
res.send({
|
2018-09-29 20:00:15 +00:00
|
|
|
error: "200",
|
2018-09-23 20:17:35 +00:00
|
|
|
data: {},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Get the data needed for the dashboard
|
2018-09-29 17:10:27 +00:00
|
|
|
userRouter.get("/dashboard", async (req: LRequest, res) => {
|
2018-09-29 13:06:14 +00:00
|
|
|
const { db, token } = req;
|
|
|
|
|
|
|
|
// Get the user
|
|
|
|
// TODO: if (user)
|
2018-09-29 17:10:27 +00:00
|
|
|
const user = await userFromSession(token, db);
|
2018-09-29 13:06:14 +00:00
|
|
|
const { classId } = user;
|
|
|
|
|
|
|
|
// Fetch the top ten of the class
|
|
|
|
const rawTopTen = await db.collection("users").find({
|
|
|
|
classId,
|
|
|
|
}, {
|
|
|
|
sort: {
|
|
|
|
score: -1,
|
|
|
|
},
|
|
|
|
limit: 10,
|
|
|
|
}).toArray();
|
|
|
|
|
|
|
|
let nr = 1;
|
|
|
|
const topTen = rawTopTen.map((user: IUser) => {
|
|
|
|
return {
|
|
|
|
username: user.username,
|
|
|
|
score: user.score,
|
2018-09-29 17:10:27 +00:00
|
|
|
// TODO: Calculate on the client?
|
2018-09-29 13:06:14 +00:00
|
|
|
level: 1,
|
|
|
|
nr: nr++,
|
|
|
|
};
|
|
|
|
});
|
2018-09-24 16:29:29 +00:00
|
|
|
|
2018-09-29 17:10:27 +00:00
|
|
|
const nextLevel = await getNextLevel(token, db);
|
2018-09-23 20:17:35 +00:00
|
|
|
res.send({
|
2018-09-24 16:29:29 +00:00
|
|
|
error: "200",
|
2018-09-23 20:17:35 +00:00
|
|
|
data: {
|
2018-09-29 17:10:27 +00:00
|
|
|
nextLevel,
|
2018-09-29 13:06:14 +00:00
|
|
|
topTen,
|
|
|
|
lastReview: user.lastReview,
|
2018-09-23 20:17:35 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-09-29 20:00:15 +00:00
|
|
|
userRouter.post("/showWelcome", async (req: LRequest, res) => {
|
|
|
|
const { db, token } = req;
|
|
|
|
|
|
|
|
// Are all arguments specified?
|
|
|
|
if (!req.body || !("state" in req.body)) {
|
|
|
|
res.send({
|
|
|
|
error: "400",
|
|
|
|
data: {
|
|
|
|
msg: "State not specified",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the user
|
|
|
|
// TODO: if (user)
|
|
|
|
const user = await userFromSession(token, db);
|
|
|
|
|
|
|
|
await db.collection("users").updateOne({
|
|
|
|
username: user.username,
|
|
|
|
}, {
|
|
|
|
$set: {
|
|
|
|
showWelcome: req.body.state,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
res.send({
|
|
|
|
error: "200",
|
|
|
|
data: {},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-09-29 20:09:49 +00:00
|
|
|
userRouter.post("/score", async (req: LRequest, res) => {
|
|
|
|
const { token, db } = req;
|
|
|
|
|
|
|
|
// Are all arguments specified
|
|
|
|
if (!req.body || !("score" in req.body)) {
|
|
|
|
res.send({
|
|
|
|
error: "400",
|
|
|
|
data: {
|
|
|
|
msg: "No score specified!",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: if (user)
|
|
|
|
const user = await userFromSession(token, db);
|
|
|
|
// Update the score
|
|
|
|
db.collection("users").updateOne({
|
|
|
|
username: user.username,
|
|
|
|
}, {
|
|
|
|
$set: {
|
|
|
|
score: req.body.score,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-09-23 20:17:35 +00:00
|
|
|
export default userRouter;
|