diff --git a/backend/db.md b/backend/db.md index 0b29c38..afb7d97 100644 --- a/backend/db.md +++ b/backend/db.md @@ -1,5 +1,5 @@ # User -`column`: users +`collection`: users ` { username: string, @@ -13,10 +13,47 @@ lastReview: { correct: number, wrong: number, - } + }, + + lastLevel: string, + + queue: number[], } ` +- queue stores IDs of the vocabulary + +# Vocab +`collection`: vocabulary +` +{ + id: string, + german: string[], + hint: string?, + mnemonic: string?, + + type: VocabType, + + latin: INomenData | IVerbData | IAdjektivData (See frontend models), +` + +- VocabType -> number? +- id == _id? + +# Levels +`collection`: levels +` +{ + level: number; + name: string; + description: string; + + vocab: number[], +} +` + +- vocab stores the IDs of the vocabulary items from the `vocabulary` collection + # Sessions column: sessions diff --git a/backend/src/api/level.ts b/backend/src/api/level.ts index b177f2e..3a74f65 100644 --- a/backend/src/api/level.ts +++ b/backend/src/api/level.ts @@ -1,17 +1,16 @@ -import { Router, Request, Response } from "express"; +import { Router, Response } from "express"; import * as bodyparser from "body-parser"; import { authRoute } from "../security/token"; +import { LRequest } from "../types/express"; + const levelRouter = Router(); levelRouter.use(bodyparser.json()); levelRouter.use(authRoute); -levelRouter.get("/:id/vocab", async (req: Request, res: Response) => { - // TODO: Implement - console.log("Stub(get): /level/:id/vocab"); - - if (!req.params) { +levelRouter.get("/:id/vocab", async (req: LRequest, res: Response) => { + if (!req.params || !req.params.id) { res.send({ error: "400", data: { @@ -21,20 +20,29 @@ levelRouter.get("/:id/vocab", async (req: Request, res: Response) => { return; } + const levelId = parseInt(req.params.id); + // TODO: Handle this + // if (levelId === NaN) { + // // Something + // } + + // Find the level + // TODO: if (level) + const { db } = req; + const level = await db.collection("levels").findOne({ + // TODO: This aint safe, boi + level: levelId, + }); + console.log(level); + + // Fetch all the vocabulary + const vocab = await db.collection("vocabulary").find({ id: { $in: level.vocab } }).toArray(); + console.log(vocab); + res.send({ error: "0", data: { - vocab: [{ - german: ["Wein"], - hint: "Worte auf '-um' sind meistens NeutrUM", - type: 0, - latin: { - grundform: "Vinum", - genitiv: "Vini", - genus: "Neutrum" - }, - id: 0 - }], + vocab, } }); }); diff --git a/backend/src/api/user.ts b/backend/src/api/user.ts index b1eb385..fa9006e 100644 --- a/backend/src/api/user.ts +++ b/backend/src/api/user.ts @@ -6,14 +6,15 @@ import { authRoute } from "../security/token"; import { userFromSession } from "../utils/user"; import { IUser } from "../models/user"; +import { LRequest } from "../types/express"; +import { Db } from "mongodb"; const userRouter = express.Router(); userRouter.use(authRoute); userRouter.use(bodyparser.json()); // Return the user object if the user is still authenticated -userRouter.get("/me", async (req, res) => { - //@ts-ignore +userRouter.get("/me", async (req: LRequest, res) => { const { db, token } = req; const session = await db.collection("sessions").findOne({ token, }); @@ -41,10 +42,10 @@ userRouter.get("/me", async (req, res) => { }); // Removes the user's session -userRouter.get("/logout", async (req, res) => { +userRouter.get("/logout", async (req: LRequest, res) => { // Try to remove the session - //@ts-ignore - await req.db.collections("sessions").findOneAndDelete({ session: req.get("Token") }); + const { db, token } = req; + await db.collection("sessions").findOneAndDelete({ token, }); res.send({ error: "0", @@ -84,42 +85,65 @@ userRouter.get("/queue", async (req, res) => { }); // Get ot set the last review results -userRouter.get("/lastReview", async (req, res) => { - console.log("STUB(get): /user/lastReview"); +userRouter.get("/lastReview", async (req: LRequest, res) => { + // TODO: if(user) + const { token, db } = req; + const user = await userFromSession(token, db); - // TODO: Stub res.send({ error: "0", - data: { - correct: 6, - wrong: 2, - }, + data: user.lastReview, }); }); -userRouter.post("/lastReview", async (req, res) => { - console.log("STUB(post): /user/lastReview"); +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, + }, + }); - // TODO: Stub res.send({ error: "0", data: {}, }); }); -// Get the next level -userRouter.get("/nextLevel", async (req, res) => { - console.log("STUB: /user/nextLevel"); +// 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 { + // TODO: if(user) + const user = await userFromSession(token, db); + const { lastLevel } = user; - // TODO: Stub + // 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); res.send({ error: "0", - data: { - name: "Test level", - desc: "Just a test", - level: 3, - - done: false, - }, + data: level, }); }); @@ -135,16 +159,12 @@ userRouter.post("/level/:id", async (req, res) => { }); // Get the data needed for the dashboard -userRouter.get("/dashboard", async (req, res) => { - console.log("SEMI-STUB(post): /user/dashboard"); - - //@ts-ignore +userRouter.get("/dashboard", async (req: LRequest, res) => { const { db, token } = req; // Get the user - const user = await userFromSession(token, db); // TODO: if (user) - + const user = await userFromSession(token, db); const { classId } = user; // Fetch the top ten of the class @@ -162,24 +182,17 @@ userRouter.get("/dashboard", async (req, res) => { return { username: user.username, score: user.score, - // TODO + // TODO: Calculate on the client? level: 1, nr: nr++, }; }); - // TODO: Stub + const nextLevel = await getNextLevel(token, db); res.send({ error: "200", data: { - // TODO: Get this some way - nextLevel: { - name: "Test level", - desc: "Just a test", - level: 3, - - done: false, - }, + nextLevel, topTen, lastReview: user.lastReview, }, diff --git a/backend/src/main.ts b/backend/src/main.ts index 8951799..5f34467 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -115,4 +115,3 @@ const assert = require('assert'); console.log("Starting on port 8080"); }); })(); - diff --git a/backend/src/models/user.ts b/backend/src/models/user.ts index 53f01d4..2bf4283 100644 --- a/backend/src/models/user.ts +++ b/backend/src/models/user.ts @@ -11,4 +11,6 @@ export interface IUser { correct: number; wrong: number; }; + + lastLevel: number; }; diff --git a/backend/src/security/auth.ts b/backend/src/security/auth.ts index afe2102..4995360 100644 --- a/backend/src/security/auth.ts +++ b/backend/src/security/auth.ts @@ -35,6 +35,7 @@ export async function performLogin(username: string, password: string, db: Db): classId: user.classId, lastReview: user.lastReview, + lastLevel: user.lastLevel, // TODO: Implement score: 4, diff --git a/backend/src/types/express.ts b/backend/src/types/express.ts new file mode 100644 index 0000000..3085e45 --- /dev/null +++ b/backend/src/types/express.ts @@ -0,0 +1,8 @@ +import { Request } from "express"; + +import { Db } from "mongodb"; + +export type LRequest = Request & { + db: Db; + token: string; +};