From 7df29877498a74805bd561d47c2f171220654e92 Mon Sep 17 00:00:00 2001 From: Julia Demianetc Date: Sun, 15 Feb 2026 18:11:07 +0100 Subject: [PATCH 1/7] list of endpoints --- .vscode/settings.json | 22 +++++ backend/package.json | 22 +++-- backend/server.js | 194 +++++++++++++++++++++++++++++++++++++++++- frontend/api.js | 0 4 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 frontend/api.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..641ca762ef --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#65c89b", + "activityBar.background": "#65c89b", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#945bc4", + "activityBarBadge.foreground": "#e7e7e7", + "commandCenter.border": "#15202b99", + "sash.hoverBorder": "#65c89b", + "statusBar.background": "#42b883", + "statusBar.foreground": "#15202b", + "statusBarItem.hoverBackground": "#359268", + "statusBarItem.remoteBackground": "#42b883", + "statusBarItem.remoteForeground": "#15202b", + "titleBar.activeBackground": "#42b883", + "titleBar.activeForeground": "#15202b", + "titleBar.inactiveBackground": "#42b88399", + "titleBar.inactiveForeground": "#15202b99" + }, + "peacock.color": "#42b883" +} diff --git a/backend/package.json b/backend/package.json index 08f29f2448..ce6e00b854 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,18 +3,22 @@ "version": "1.0.0", "description": "Server part of final project", "scripts": { - "start": "babel-node server.js", - "dev": "nodemon server.js --exec babel-node" + "start": "nodemon server.js --exec babel-node", + "dev": "nodemon server.js --exec babel-node", + "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { - "@babel/core": "^7.17.9", - "@babel/node": "^7.16.8", - "@babel/preset-env": "^7.16.11", + "@babel/core": "^7.29.0", + "@babel/node": "^7.29.0", + "@babel/preset-env": "^7.29.0", "cors": "^2.8.5", - "express": "^4.17.3", - "mongoose": "^8.4.0", - "nodemon": "^3.0.1" + "express": "^4.22.1", + "express-list-endpoints": "^7.1.1", + "mongodb": "^7.1.0", + "mongoose": "^8.23.0", + "nodemon": "^3.1.11", + "test": "^3.3.0" } -} \ No newline at end of file +} diff --git a/backend/server.js b/backend/server.js index 070c875189..761de31853 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,6 +1,7 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; +import listEndpoints from "express-list-endpoints"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; mongoose.connect(mongoUrl); @@ -12,8 +13,199 @@ const app = express(); app.use(cors()); app.use(express.json()); +// TODO ---- AUTHORISATION MIDDLEWARE + +// DONE ---- All ENDPOINTS, temporarily ---- + app.get("/", (req, res) => { - res.send("Hello Technigo!"); + const endpoints = listEndpoints(app); + console.log({ endpoints: endpoints }); + res.json(endpoints); // FIXME delete res.json for production +}); + +// TODO ---- POST ENDPOINTS ---- + +// ---- USER ---- + +// FIXME MUST ---- Register new user +app.post("/register", (req, res) => { + console.log("register"); +}); + +// FIXME MUST ---- Login with existing user +app.post("/login", (req, res) => { + console.log("login"); +}); + +// ---- QUESTS ---- + +// FIXME MUST --- Create a quest >>>>> only for auth users +app.post("/quests", (res, req) => { + console.log("create a quest at main page"); +}); + +// FIXME MUST ---- Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users +app.post("/quests/random", (req, res) => { + console.log("randomize quest and select by time"); +}); + +// FIXME MUST ---- Re-try to get a new quest >>>>> only for auth users +app.post("quests/random/:sessionId/retry", (req, res) => { + console.log("re-try"); +}); + +// FIXME ---- Complete task >>>>> only for auth users +app.post("quests/:questid/complete", (req, res) => { + console.log("Task is done"); +}); + +//FIXME NICE+ ---- User completes task too fast confirmation >>>>> only for auth users +app.post("quests/:questid/confirm-complete", (req, res) => { + console.log("Do not cheat, ok?"); +}); + +// FIXME EXTRA ---- Add actual time >>>>> only for auth users +app.post("quests/:questid/add-time", (req, res) => { + console.log("Add actual time"); +}); + +// FIXME NICE+ ---- Skip a day of quests >>>>> only for auth users +app.post("/quests/skip"); + +// FIXME NICE+ ---- Repetitive quests >>>>> only for auth users +app.post("/quests/:questid/repeat"); + +// ---- FRIENDS ---- +// FIXME MUST --- Give kudos >>>>> only for auth users +app.post("/friends/:postid/kudos", (req, res) => { + console.log("Give kudos"); +}); + +// ---- PUNISHMENTS ---- +// EXTRA ---- Send an embarrassing message to smbdy >>>>> only for auth users +app.post("/punishment/embarrass-me", (req, res) => { + console.log("Welp it didn't go well"); +}); + +// EXTRA ---- Lock instagram or tiktok for an hour >>>>> only for auth users +app.post("/punishment/lock", (req, res) => { + console.log("def too much"); +}); + +// TODO ---- GET ENDPOINTS ---- + +// ---- MAIN PAGES ---- + +// FIXME MUST ---- Home page=main page=welcome page +app.get("/home", (res, req) => { + console.log("Main=welcome=home page"); +}); + +// FIXME MUST ---- About app page +app.get("/about", (req, res) => { + console.log("about page"); +}); + +// FIXME Nice+ ---- User page (shows: current strike, settings, log out, delete user, bonus points, profile picture state, user library) >>>>> only for auth users +app.get("/profile/:userid", (req, res) => { + console.log("user info page"); +}); + +// ---- USER ---- +// FIXME ---- Smiley state of mood ---- >>>> only for auth users, returns sad/happy/delighted avatars +app.get("/user/:id/state", (req, res) => { + console.log("this is your mode"); +}); + +// ---- FRIENDS ---- + +// FIXME MUST ---- Friends Feed page (alt: any other users feed?) >>>>> only for auth users( if it's a friends page, otherwise for everybody?) +app.get("/friends", (res, req) => { + console.log("Friends feed page"); +}); + +// FIXME NICE+ ---- Find a friend bi ID page +app.get("friends/:friendid", (res, req) => { + console.log("friend by id"); +}); + +// FIXME NICE+ ---- Find a friend by :name page +app.get("/friends/:name", (req, res) => { + console.log("friend by name"); +}); + +// ---- QUESTS ---- + +// FIXME MUST ---- Quests default library (returns default tasks, categories, est time) +app.get("/quests/library", (req, res) => { + console.log("Default quest library"); +}); + +// FIXME MUST --- User created quests (returns defaut tasks user added, user created tasks, categories, est time) >>>>> only for auth users +app.get("quests/user/:userid/", (req, res) => { + console.log("User's quests full list"); +}); + +// FIXME MUST ---- Users one quest >>>>> only for auth users +app.get("/user/:id/quests/:id", (req, res) => { + console.log("My one quest of the day"); +}); + +// FIXME MUST ---- Rewards >>>>> only for auth users +app.get("/rewards", (req, res) => { + console.log("Your reward is here"); +}); + +// FIXME MUST ---- Streaks >>>>> only for auth users +app.get("/streaks", (req, res) => { + console.log("Your streak"); +}); + +// FIXME NICE+ ---- Quests history >>>>>> only for auth users +app.get("/quests/history", (req, res) => { + console.log("Shows how much user have done before"); +}); + +// TODO ---- DELETE ENDPOINTS ---- + +// ---- USER ---- + +// FIXME MUST ---- Delete user >>>> only for auth user +app.delete("/user/:id", (req, res) => { + console.log("auth by id and delete user"); +}); + +// ---- QUESTS ---- + +// FIXME MUST ---- Delete one quest >>>>> only for authorised users for their list +app.delete("/user/:id/quests/:id", (req, res) => { + console.log("delete test"); +}); + +// FIXME EXTRA ---- DELETE more than 1 quest at a time >>>>> only for authorised users for their list +app.delete("/user/:id/quests/", (req, res) => { + console.log("Delete user's quests"); +}); + +// ---- FRIENDS ---- + +// FIXME NICE+ ---- Delete a friend >>>>> only for authorised users for their feed +app.delete("/friends/:id", (req, res) => { + console.log("delete friend"); +}); + +// TODO ---- PUT ENDPOINTS ---- + +// TODO ---- PATCH ENDPOINTS ---- + +// FIXME EXTRA ---- Edit profile >>>>> only for authorised users for their own profiles(toggle easy/hard mode, change password?) +app.patch("/profile/:id/settings", (req, res) => { + console.log("edit profile"); +}); + +// FIXME NICE+ ---- Edit one quest >>>>> only for authorised users for their list +app.patch("/user/:id/quests/:id", (req, res) => { + console.log("delete test"); }); // Start the server diff --git a/frontend/api.js b/frontend/api.js new file mode 100644 index 0000000000..e69de29bb2 From 9df6ed7ffb75a8219973f2d7c345c53a1839c18f Mon Sep 17 00:00:00 2001 From: Julia Demianetc Date: Sun, 15 Feb 2026 22:21:09 +0100 Subject: [PATCH 2/7] added quest and user schemas --- backend/schemas.js | 93 ++++++++++++++++++++++++++++++++++++++++++++++ backend/server.js | 70 ++++++++++++++++++---------------- 2 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 backend/schemas.js diff --git a/backend/schemas.js b/backend/schemas.js new file mode 100644 index 0000000000..3db86c908a --- /dev/null +++ b/backend/schemas.js @@ -0,0 +1,93 @@ +import mongoose from "mongoose"; + +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; +mongoose.connect(mongoUrl); +mongoose.Promise = Promise; + +// FIXME MUST ---- Quest ---- + +const questSchema = new mongoose.Schema( + { + message: { + type: String, + required: true, + minLength: 2, + }, + + timeNeeded: { + type: Number, + require: true, + }, + + category: { + type: String, + }, + + deadline: { + type: Date, + require: false, + }, + + done: { + type: Boolean, + default: false, + }, + }, + + { timestamps: true }, +); + +export const Quest = mongoose.model("Quest", questSchema); + +// FIXME MUST ---- User ---- + +const userSchema = new mongoose.Schema( + { + name: { + type: String, + unique: true, + required: true, + minLength: 3, + maxLength: 24, + }, + + email: { + type: String, + required: true, + unique: true, + }, + + password: { + type: String, + required: true, + minLength: 8, + }, + + registerDate: { + type: Date, + default: () => new Date(), + }, + + streak: { + type: Number, + default: 0, + }, + + todayTaskCompleted: { + type: Boolean, + default: false, + }, + + lastTaskCompleted: { + type: Date, + }, + + moodUrl: String, + }, + + { timestamps: true }, +); + +export const User = mongoose.model("User", userSchema); + +// FIXME ???NICE+ ---- Friend ----- diff --git a/backend/server.js b/backend/server.js index 761de31853..e35443cb09 100644 --- a/backend/server.js +++ b/backend/server.js @@ -2,6 +2,8 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; import listEndpoints from "express-list-endpoints"; +import { Quest } from "./schemas"; +import { User } from "./schemas"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; mongoose.connect(mongoUrl); @@ -25,7 +27,7 @@ app.get("/", (req, res) => { // TODO ---- POST ENDPOINTS ---- -// ---- USER ---- +// TODO ---- USER ---- // FIXME MUST ---- Register new user app.post("/register", (req, res) => { @@ -37,11 +39,11 @@ app.post("/login", (req, res) => { console.log("login"); }); -// ---- QUESTS ---- +// TODO ---- QUESTS ---- // FIXME MUST --- Create a quest >>>>> only for auth users -app.post("/quests", (res, req) => { - console.log("create a quest at main page"); +app.post("/quests", (req, res) => { + res.send("Create your quest"); }); // FIXME MUST ---- Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users @@ -81,7 +83,7 @@ app.post("/friends/:postid/kudos", (req, res) => { console.log("Give kudos"); }); -// ---- PUNISHMENTS ---- +// TODO ---- PUNISHMENTS ---- // EXTRA ---- Send an embarrassing message to smbdy >>>>> only for auth users app.post("/punishment/embarrass-me", (req, res) => { console.log("Welp it didn't go well"); @@ -94,38 +96,42 @@ app.post("/punishment/lock", (req, res) => { // TODO ---- GET ENDPOINTS ---- -// ---- MAIN PAGES ---- - -// FIXME MUST ---- Home page=main page=welcome page -app.get("/home", (res, req) => { - console.log("Main=welcome=home page"); -}); - -// FIXME MUST ---- About app page -app.get("/about", (req, res) => { - console.log("about page"); +// TODO ---- MAIN PAGES ---- +// ---- USER ---- +// FIXME ---- Smiley state of mood ---- >>>> only for auth users, returns sad/happy/delighted avatars +app.get("/user/:id/state", (req, res) => { + /* console.log("this is your mode"); */ + res.send("User mood"); }); // FIXME Nice+ ---- User page (shows: current strike, settings, log out, delete user, bonus points, profile picture state, user library) >>>>> only for auth users app.get("/profile/:userid", (req, res) => { - console.log("user info page"); + /* console.log("user info page"); */ + res.send("User profle"); }); -// ---- USER ---- -// FIXME ---- Smiley state of mood ---- >>>> only for auth users, returns sad/happy/delighted avatars -app.get("/user/:id/state", (req, res) => { - console.log("this is your mode"); -}); - -// ---- FRIENDS ---- +// TODO ---- FRIENDS ---- // FIXME MUST ---- Friends Feed page (alt: any other users feed?) >>>>> only for auth users( if it's a friends page, otherwise for everybody?) -app.get("/friends", (res, req) => { - console.log("Friends feed page"); +app.get("/friends", (req, res) => { + res.json([ + { + name: "Jane", + quest: "Dust your books", + kudos: "5", + doneAt: "2026-02-15", + }, + { + name: "John", + quest: "Clean the kitchen", + kudos: "38", + doneAt: "2026-02-15", + }, + ]); }); // FIXME NICE+ ---- Find a friend bi ID page -app.get("friends/:friendid", (res, req) => { +app.get("/friends/:friendid", (req, res) => { console.log("friend by id"); }); @@ -134,7 +140,7 @@ app.get("/friends/:name", (req, res) => { console.log("friend by name"); }); -// ---- QUESTS ---- +// TODO ---- QUESTS ---- // FIXME MUST ---- Quests default library (returns default tasks, categories, est time) app.get("/quests/library", (req, res) => { @@ -142,8 +148,8 @@ app.get("/quests/library", (req, res) => { }); // FIXME MUST --- User created quests (returns defaut tasks user added, user created tasks, categories, est time) >>>>> only for auth users -app.get("quests/user/:userid/", (req, res) => { - console.log("User's quests full list"); +app.get("/quests", (req, res) => { + res.send("This is a list of your quests"); }); // FIXME MUST ---- Users one quest >>>>> only for auth users @@ -168,14 +174,14 @@ app.get("/quests/history", (req, res) => { // TODO ---- DELETE ENDPOINTS ---- -// ---- USER ---- +// TODO ---- USER ---- // FIXME MUST ---- Delete user >>>> only for auth user app.delete("/user/:id", (req, res) => { console.log("auth by id and delete user"); }); -// ---- QUESTS ---- +// TODO ---- QUESTS ---- // FIXME MUST ---- Delete one quest >>>>> only for authorised users for their list app.delete("/user/:id/quests/:id", (req, res) => { @@ -187,7 +193,7 @@ app.delete("/user/:id/quests/", (req, res) => { console.log("Delete user's quests"); }); -// ---- FRIENDS ---- +// TODO ---- FRIENDS ---- // FIXME NICE+ ---- Delete a friend >>>>> only for authorised users for their feed app.delete("/friends/:id", (req, res) => { From 4d131cb492951894601dadc3cdadfa762e561d42 Mon Sep 17 00:00:00 2001 From: Julia Demianetc Date: Mon, 16 Feb 2026 18:10:50 +0100 Subject: [PATCH 3/7] Added authorization, user routes --- backend/authMiddleware.js | 12 ++++ backend/package.json | 3 + backend/quests.json | 54 +++++++++++++++++ backend/schemas.js | 17 +++++- backend/server.js | 124 ++++++++++++++++++++++++++++++-------- 5 files changed, 183 insertions(+), 27 deletions(-) create mode 100644 backend/authMiddleware.js create mode 100644 backend/quests.json diff --git a/backend/authMiddleware.js b/backend/authMiddleware.js new file mode 100644 index 0000000000..0ac68b9bb9 --- /dev/null +++ b/backend/authMiddleware.js @@ -0,0 +1,12 @@ +import { User } from "./schemas"; + +// FIXME - add error handling, put accesstoken req into variable +export const authentificateUser = async (req, res, next) => { + const user = await User.findOne({ accessToken: req.header("Authorization") }); + if (user) { + req.user = user; + next(); + } else { + res.status(401).json({ loggedOut: true }); + } +}; diff --git a/backend/package.json b/backend/package.json index ce6e00b854..4c8b5d1a4d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,7 +13,10 @@ "@babel/core": "^7.29.0", "@babel/node": "^7.29.0", "@babel/preset-env": "^7.29.0", + "bcrypt": "^6.0.0", + "bcrypt-nodejs": "^0.0.3", "cors": "^2.8.5", + "crypto": "^1.0.1", "express": "^4.22.1", "express-list-endpoints": "^7.1.1", "mongodb": "^7.1.0", diff --git a/backend/quests.json b/backend/quests.json new file mode 100644 index 0000000000..51bf4df4bb --- /dev/null +++ b/backend/quests.json @@ -0,0 +1,54 @@ +[ + { + "_id": "682bab8c12155b00101732cb", + "message": "Clean the toilet", + "timeNeed": 20, + "category": ["Cleaning", "Bathroom"], + "deadline": "2026-03-15T22:07:08.999Z", + "done": "true", + "createdAt": "2026-03-08T22:07:08.999Z", + "updatedAt": "2025-03-14T22:07:08.999Z", + "createdBy": "", + "__v": 0 + }, + { + "_id": "682bab8c12155b00101732cb", + "message": "Do one laundry run", + "timeNeed": 12, + "category": ["Washing", "Clothes"], + "deadline": "2026-03-17T22:07:08.999Z", + "done": "false", + "createdAt": "2026-03-08T22:07:08.999Z", + "__v": 0 + }, + { + "_id": "682bab8c12155b00101732cb", + "message": "Vacuum clean bedroom", + "timeNeed": 25, + "category": ["Cleaning", "Bedroom"], + "deadline": "2026-03-10T22:07:08.999Z", + "done": "true", + "createdAt": "2026-03-04T22:07:08.999Z", + "updatedAt": "2025-03-09T22:07:08.999Z", + "__v": 0 + }, + { + "_id": "682bab8c12155b00101732cb", + "message": "Water your plants", + "timeNeed": 5, + "category": ["Plants"], + "done": "false", + "createdAt": "2026-03-08T22:07:08.999Z", + "__v": 0 + }, + { + "_id": "682bab8c12155b00101732cb", + "message": "Unload dishwasher", + "timeNeed": 16, + "category": ["Cleaning", "Bathroom"], + "done": "true", + "createdAt": "2026-03-08T22:07:08.999Z", + "updatedAt": "2025-03-09T22:07:08.999Z", + "__v": 0 + } +] diff --git a/backend/schemas.js b/backend/schemas.js index 3db86c908a..29b11de192 100644 --- a/backend/schemas.js +++ b/backend/schemas.js @@ -1,4 +1,6 @@ import mongoose from "mongoose"; +import crypto from "crypto"; +import bcrypt from "bcrypt-nodejs"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; mongoose.connect(mongoUrl); @@ -32,6 +34,11 @@ const questSchema = new mongoose.Schema( type: Boolean, default: false, }, + + createdBy: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + }, }, { timestamps: true }, @@ -83,6 +90,11 @@ const userSchema = new mongoose.Schema( }, moodUrl: String, + + accessToken: { + type: String, + default: () => crypto.randomBytes(128).toString("hex"), + }, }, { timestamps: true }, @@ -90,4 +102,7 @@ const userSchema = new mongoose.Schema( export const User = mongoose.model("User", userSchema); -// FIXME ???NICE+ ---- Friend ----- +// TODO ---- Session ---- +// Schema to randomize and sessions? + +// TODO ???NICE+ ---- Friends ----- diff --git a/backend/server.js b/backend/server.js index e35443cb09..119e3a63ee 100644 --- a/backend/server.js +++ b/backend/server.js @@ -4,6 +4,11 @@ import mongoose from "mongoose"; import listEndpoints from "express-list-endpoints"; import { Quest } from "./schemas"; import { User } from "./schemas"; +import quests from "./quests.json"; +import bcrypt from "bcrypt-nodejs"; +import { authentificateUser } from "./authMiddleware"; + +//console.log("Quests: ", quests.length); const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; mongoose.connect(mongoUrl); @@ -15,7 +20,14 @@ const app = express(); app.use(cors()); app.use(express.json()); -// TODO ---- AUTHORISATION MIDDLEWARE +//Middleware to hadnle error at service availability before running anything else +app.use((req, res, next) => { + if (mongoose.connection.readyState === 1) { + next(); + } else { + res.status(503).json({ error: "Service unavailable" }); + } +}); // DONE ---- All ENDPOINTS, temporarily ---- @@ -29,14 +41,46 @@ app.get("/", (req, res) => { // TODO ---- USER ---- -// FIXME MUST ---- Register new user -app.post("/register", (req, res) => { - console.log("register"); +// FIXME MUST ---- Register new user ---- +app.post("/signup", async (req, res) => { + try { + const { name, email, password } = req.body; + + //One-way encryption: + const salt = bcrypt.genSaltSync(); + const hashedPass = bcrypt.hashSync(password, salt); + + const user = new User({ + name, + email, + password: hashedPass, + }); + await user.save(); + res.status(201).json({ id: user._id, accessToken: user.accessToken }); + } catch (err) { + res + .status(400) + .json({ message: "Could not create user", errors: err.errors }); + } }); -// FIXME MUST ---- Login with existing user -app.post("/login", (req, res) => { - console.log("login"); +// FIXME Update error handling MUST ---- Login with existing user ---- +app.post("/login", async (req, res) => { + const { email, password } = req.body; + const user = await User.findOne({ email }); //retrieving from database by email, should be unique + try { + if (user && bcrypt.compareSync(password, user.password)) { + //Success + res.json({ userID: user._id, accessToken: user.accessToken }); + } else { + //Failed: + //1.User doesn't exist + //2.Password doesn't match + res.json({ notFound: true }); + } + } catch (err) { + res.status(400).json({ err: "Bad request" }); + } }); // TODO ---- QUESTS ---- @@ -46,17 +90,17 @@ app.post("/quests", (req, res) => { res.send("Create your quest"); }); -// FIXME MUST ---- Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users +// FIXME MUST ---- ??? is it post? Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users app.post("/quests/random", (req, res) => { - console.log("randomize quest and select by time"); + res.send("Random quest"); }); -// FIXME MUST ---- Re-try to get a new quest >>>>> only for auth users +// FIXME MUST ---- ??? is it post? Re-try to get a new quest >>>>> only for auth users app.post("quests/random/:sessionId/retry", (req, res) => { console.log("re-try"); }); -// FIXME ---- Complete task >>>>> only for auth users +// FIXME ---- Complete a quest >>>>> only for auth users app.post("quests/:questid/complete", (req, res) => { console.log("Task is done"); }); @@ -79,7 +123,7 @@ app.post("/quests/:questid/repeat"); // ---- FRIENDS ---- // FIXME MUST --- Give kudos >>>>> only for auth users -app.post("/friends/:postid/kudos", (req, res) => { +app.post("/friends/:postid/kudos", authentificateUser, (req, res) => { console.log("Give kudos"); }); @@ -131,44 +175,72 @@ app.get("/friends", (req, res) => { }); // FIXME NICE+ ---- Find a friend bi ID page -app.get("/friends/:friendid", (req, res) => { - console.log("friend by id"); +app.get("/friends/:friendid", async (req, res) => { + const friend = await User.findById(req.params.id); //search through users ids in database? + res.json(friend); }); // FIXME NICE+ ---- Find a friend by :name page -app.get("/friends/:name", (req, res) => { - console.log("friend by name"); +app.get("/friends/:name", async (req, res) => { + const friend = await User.findOne(req.params.name); //search through users names in database? + res.json(friend); }); // TODO ---- QUESTS ---- // FIXME MUST ---- Quests default library (returns default tasks, categories, est time) app.get("/quests/library", (req, res) => { - console.log("Default quest library"); + res.json(quests); +}); + +// FIXME MUST --- User's quests (returns defaut tasks user added, user created tasks, categories, est time) >>>>> only for auth users +app.get("/user/:id/quests/", authentificateUser, async (req, res) => { + //const userQuests = await Quest.find().populate("createdBy"); + //res.send(userQuests); + const user = await User.findById(req.params.id); + const quests = await Quest.find({ + user: mongoose.Types.ObjectId.createFromHexString(user.id), + }); + try { + if (user) { + res.json(quests); + } else { + res.status(404).json({ error: "Oops, user not found" }); + } + } catch (err) { + res.status(400).json({ error: "Invalid user ID" }); + } +}); + +// FIXME MUST ---- Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users +app.get("/quests/random", (req, res) => { + res.send("Random quest"); }); -// FIXME MUST --- User created quests (returns defaut tasks user added, user created tasks, categories, est time) >>>>> only for auth users -app.get("/quests", (req, res) => { - res.send("This is a list of your quests"); +// FIXME MUST ---- User's done quest /user/:id/quests/done/:done +app.get("quests/done/:done", (req, res) => { + const done = req.params.done; + const questsDone = quests.filter((item) => item.done === done); + res.json(questsDone); }); -// FIXME MUST ---- Users one quest >>>>> only for auth users +// FIXME MUST ---- User's daily random(!) quest >>>>> only for auth users app.get("/user/:id/quests/:id", (req, res) => { - console.log("My one quest of the day"); + res.send("My one quest of the day"); }); // FIXME MUST ---- Rewards >>>>> only for auth users -app.get("/rewards", (req, res) => { - console.log("Your reward is here"); +app.get("user/:id/rewards", (req, res) => { + res.send("Your reward is here"); }); // FIXME MUST ---- Streaks >>>>> only for auth users -app.get("/streaks", (req, res) => { +app.get("user/:id/streaks", (req, res) => { console.log("Your streak"); }); // FIXME NICE+ ---- Quests history >>>>>> only for auth users -app.get("/quests/history", (req, res) => { +app.get("user/:id/quests/history", (req, res) => { console.log("Shows how much user have done before"); }); From a8adfdedee0bc2c239371bcf4a33ce27a682698f Mon Sep 17 00:00:00 2001 From: Julia Demianetc Date: Mon, 16 Feb 2026 22:13:24 +0100 Subject: [PATCH 4/7] updated main routes --- backend/quests.json | 10 +-- backend/schemas.js | 8 +- backend/server.js | 198 ++++++++++++++++++++++++++++---------------- 3 files changed, 136 insertions(+), 80 deletions(-) diff --git a/backend/quests.json b/backend/quests.json index 51bf4df4bb..012676fc01 100644 --- a/backend/quests.json +++ b/backend/quests.json @@ -1,6 +1,6 @@ [ { - "_id": "682bab8c12155b00101732cb", + "_id": "682bab8c12155b00101732db", "message": "Clean the toilet", "timeNeed": 20, "category": ["Cleaning", "Bathroom"], @@ -12,7 +12,7 @@ "__v": 0 }, { - "_id": "682bab8c12155b00101732cb", + "_id": "682bab8c12155b00101732nb", "message": "Do one laundry run", "timeNeed": 12, "category": ["Washing", "Clothes"], @@ -33,7 +33,7 @@ "__v": 0 }, { - "_id": "682bab8c12155b00101732cb", + "_id": "682bab8c12155b00101732kb", "message": "Water your plants", "timeNeed": 5, "category": ["Plants"], @@ -42,10 +42,10 @@ "__v": 0 }, { - "_id": "682bab8c12155b00101732cb", + "_id": "682bab8c12155b00101732db", "message": "Unload dishwasher", "timeNeed": 16, - "category": ["Cleaning", "Bathroom"], + "category": ["Cleaning"], "done": "true", "createdAt": "2026-03-08T22:07:08.999Z", "updatedAt": "2025-03-09T22:07:08.999Z", diff --git a/backend/schemas.js b/backend/schemas.js index 29b11de192..f0907e878b 100644 --- a/backend/schemas.js +++ b/backend/schemas.js @@ -21,9 +21,11 @@ const questSchema = new mongoose.Schema( require: true, }, - category: { - type: String, - }, + category: [ + { + type: String, + }, + ], deadline: { type: Date, diff --git a/backend/server.js b/backend/server.js index 119e3a63ee..0fc8bd4de2 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,4 +1,4 @@ -import express from "express"; +import express, { json } from "express"; import cors from "cors"; import mongoose from "mongoose"; import listEndpoints from "express-list-endpoints"; @@ -29,19 +29,22 @@ app.use((req, res, next) => { } }); -// DONE ---- All ENDPOINTS, temporarily ---- +// ---- All ENDPOINTS, temporarily ---- app.get("/", (req, res) => { const endpoints = listEndpoints(app); console.log({ endpoints: endpoints }); - res.json(endpoints); // FIXME delete res.json for production + res.json({ + message: "List of all endpoints", + endpoints: endpoints, + }); // FIXME delete res.json before prod! }); // TODO ---- POST ENDPOINTS ---- // TODO ---- USER ---- -// FIXME MUST ---- Register new user ---- +// FIXME update error handling and more MUST ---- Register new user ---- app.post("/signup", async (req, res) => { try { const { name, email, password } = req.body; @@ -64,7 +67,7 @@ app.post("/signup", async (req, res) => { } }); -// FIXME Update error handling MUST ---- Login with existing user ---- +// FIXME Update error handling / MUST ---- Login with existing user ---- app.post("/login", async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); //retrieving from database by email, should be unique @@ -76,7 +79,7 @@ app.post("/login", async (req, res) => { //Failed: //1.User doesn't exist //2.Password doesn't match - res.json({ notFound: true }); + res.json({ message: "Smth went wrong, check your email and password" }); } } catch (err) { res.status(400).json({ err: "Bad request" }); @@ -85,9 +88,37 @@ app.post("/login", async (req, res) => { // TODO ---- QUESTS ---- -// FIXME MUST --- Create a quest >>>>> only for auth users -app.post("/quests", (req, res) => { - res.send("Create your quest"); +// FIXME Update to auth unauthorized users // MUST --- Create a quest >>>>> only for auth users +app.post("/quests", async (req, res) => { + //res.send("Create your quest"); + //const body = req.body; + //res.json(body); + const { message, timeNeeded, category, deadline } = req.body; + + try { + const quest = await new Quest({ + message, + timeNeeded, + category, + deadline, + }).save(); + res.status(201).json(quest); + } catch (err) { + res.status(400).json({ + message: "Couldn't save quest, please try again", + error: err.errors, + }); + } +}); + +// FIXME MUST ---- Add quest from default library to user's list >>>>> only for auth users +app.post("/quests/library/add", (req, res) => { + console.log("Add quest from default library to user's list"); +}); + +// FIXME ---- Complete a quest >>>>> only for auth users +app.patch("quests/:questid/complete", (req, res) => { + console.log("Task is done"); }); // FIXME MUST ---- ??? is it post? Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users @@ -100,11 +131,6 @@ app.post("quests/random/:sessionId/retry", (req, res) => { console.log("re-try"); }); -// FIXME ---- Complete a quest >>>>> only for auth users -app.post("quests/:questid/complete", (req, res) => { - console.log("Task is done"); -}); - //FIXME NICE+ ---- User completes task too fast confirmation >>>>> only for auth users app.post("quests/:questid/confirm-complete", (req, res) => { console.log("Do not cheat, ok?"); @@ -140,6 +166,92 @@ app.post("/punishment/lock", (req, res) => { // TODO ---- GET ENDPOINTS ---- +// TODO ---- QUESTS ---- + +// FIXME add error handling // MUST ---- Quests default library (returns default tasks, categories, est time), can filter on one category and time <= N +app.get("/quests/library", (req, res) => { + const { category, time } = req.query; + //console.log("category", category); + let filteredQuests = quests; + + //Test example: http://localhost:8080/quests/library/?category=cleaning&time=20 + if (category) { + filteredQuests = filteredQuests.filter((item) => { + return item.category.some((word) => { + return word.toLowerCase() === category.toLowerCase(); + }); + }); + } + + if (time) { + filteredQuests = filteredQuests.filter((item) => { + return item.timeNeed <= Number(time); + }); + } + res.json(filteredQuests); +}); + +// FIXME MUST --- User's quests (returns all tasks user saved to their list, both from library and user-created, with categories and est time; can filter on categories and time <= N) >>>>> only for auth users +//Currently: only checks build-in library +app.get("/user/:id/quests/", authentificateUser, async (req, res) => { + //const userQuests = await Quest.find().populate("createdBy"); + //res.send(userQuests); + const user = await User.findById(req.params.id); + const quests = await Quest.find({ + user: mongoose.Types.ObjectId.createFromHexString(user.id), + }); + try { + if (user) { + res.json(quests); + } else { + res.status(404).json({ error: "Oops, user not found" }); + } + } catch (err) { + res.status(400).json({ error: "Invalid user ID" }); + } +}); + +// FIXME MUST ---- User's done quests /user/:id/quests/done/true +app.get("quests/done/:done", (req, res) => { + const done = req.params.done; + const questsDone = quests.filter((item) => item.done === done); + res.json(questsDone); +}); + +// FIXME add randomizing(?here?), add looking through all database update error handling // MUST ---- User's daily random(!) quest >>>>> only for auth users // "/user/:userId/quests/:questId" +//NOW: only finds one from in-build library +app.get("/quests/:questId", authentificateUser, (req, res) => { + //res.send("My one random quest of the day"); + const id = req.params.questId; + + try { + const dailyQuest = quests.find((item) => item._id === id); + + if (!dailyQuest) { + return res.status(404).json({ error: `Quest with ${id} is not found` }); + } + + res.json(dailyQuest); + } catch (err) { + res.status(400).json({ error: `Something went wrong, ${id} is not valid` }); + } +}); + +// FIXME MUST ---- User's Rewards Collection >>>>> only for auth users +app.get("user/:id/rewards", (req, res) => { + res.send("Your reward is here"); +}); + +// FIXME MUST ---- Streaks >>>>> only for auth users +app.get("user/:id/streaks", (req, res) => { + console.log("Your streak"); +}); + +// FIXME NICE+ ---- Quests history >>>>>> only for auth users +app.get("user/:id/quests/history", (req, res) => { + console.log("Shows how much user have done before"); +}); + // TODO ---- MAIN PAGES ---- // ---- USER ---- // FIXME ---- Smiley state of mood ---- >>>> only for auth users, returns sad/happy/delighted avatars @@ -186,64 +298,6 @@ app.get("/friends/:name", async (req, res) => { res.json(friend); }); -// TODO ---- QUESTS ---- - -// FIXME MUST ---- Quests default library (returns default tasks, categories, est time) -app.get("/quests/library", (req, res) => { - res.json(quests); -}); - -// FIXME MUST --- User's quests (returns defaut tasks user added, user created tasks, categories, est time) >>>>> only for auth users -app.get("/user/:id/quests/", authentificateUser, async (req, res) => { - //const userQuests = await Quest.find().populate("createdBy"); - //res.send(userQuests); - const user = await User.findById(req.params.id); - const quests = await Quest.find({ - user: mongoose.Types.ObjectId.createFromHexString(user.id), - }); - try { - if (user) { - res.json(quests); - } else { - res.status(404).json({ error: "Oops, user not found" }); - } - } catch (err) { - res.status(400).json({ error: "Invalid user ID" }); - } -}); - -// FIXME MUST ---- Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users -app.get("/quests/random", (req, res) => { - res.send("Random quest"); -}); - -// FIXME MUST ---- User's done quest /user/:id/quests/done/:done -app.get("quests/done/:done", (req, res) => { - const done = req.params.done; - const questsDone = quests.filter((item) => item.done === done); - res.json(questsDone); -}); - -// FIXME MUST ---- User's daily random(!) quest >>>>> only for auth users -app.get("/user/:id/quests/:id", (req, res) => { - res.send("My one quest of the day"); -}); - -// FIXME MUST ---- Rewards >>>>> only for auth users -app.get("user/:id/rewards", (req, res) => { - res.send("Your reward is here"); -}); - -// FIXME MUST ---- Streaks >>>>> only for auth users -app.get("user/:id/streaks", (req, res) => { - console.log("Your streak"); -}); - -// FIXME NICE+ ---- Quests history >>>>>> only for auth users -app.get("user/:id/quests/history", (req, res) => { - console.log("Shows how much user have done before"); -}); - // TODO ---- DELETE ENDPOINTS ---- // TODO ---- USER ---- From 91fd7986cc714a662d4fd98101257c414574e8bd Mon Sep 17 00:00:00 2001 From: Julia Demianetc Date: Mon, 16 Feb 2026 23:19:22 +0100 Subject: [PATCH 5/7] updated more routes --- backend/server.js | 55 +++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/backend/server.js b/backend/server.js index 0fc8bd4de2..91e5d17b3a 100644 --- a/backend/server.js +++ b/backend/server.js @@ -8,8 +8,6 @@ import quests from "./quests.json"; import bcrypt from "bcrypt-nodejs"; import { authentificateUser } from "./authMiddleware"; -//console.log("Quests: ", quests.length); - const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; @@ -20,7 +18,7 @@ const app = express(); app.use(cors()); app.use(express.json()); -//Middleware to hadnle error at service availability before running anything else +//Middleware to handle error at service availability before running anything else app.use((req, res, next) => { if (mongoose.connection.readyState === 1) { next(); @@ -88,11 +86,8 @@ app.post("/login", async (req, res) => { // TODO ---- QUESTS ---- -// FIXME Update to auth unauthorized users // MUST --- Create a quest >>>>> only for auth users +// FIXME Update to auth authorized users, add error handling or redirecting for not authorized // MUST --- Create a quest >>>>> only for auth users app.post("/quests", async (req, res) => { - //res.send("Create your quest"); - //const body = req.body; - //res.json(body); const { message, timeNeeded, category, deadline } = req.body; try { @@ -116,11 +111,16 @@ app.post("/quests/library/add", (req, res) => { console.log("Add quest from default library to user's list"); }); -// FIXME ---- Complete a quest >>>>> only for auth users -app.patch("quests/:questid/complete", (req, res) => { +// FIXME MUST ---- Complete a quest >>>>> only for auth users +app.post("quests/:questid/complete", (req, res) => { console.log("Task is done"); }); +//FIXME NICE+ ---- User completes task too fast confirmation >>>>> only for auth users +app.post("quests/:questid/confirm-complete", (req, res) => { + console.log("Do not cheat, ok?"); +}); + // FIXME MUST ---- ??? is it post? Quests randomization, (filter tasks =< time available today; re-try rule; randomization session with sessionId), >>>>> only for auth users app.post("/quests/random", (req, res) => { res.send("Random quest"); @@ -218,11 +218,11 @@ app.get("quests/done/:done", (req, res) => { res.json(questsDone); }); -// FIXME add randomizing(?here?), add looking through all database update error handling // MUST ---- User's daily random(!) quest >>>>> only for auth users // "/user/:userId/quests/:questId" +// FIXME add randomizing, add looking through all database update error handling // MUST ---- User's daily random(!) quest >>>>> only for auth users // "/user/:userId/quests/:questId" //NOW: only finds one from in-build library -app.get("/quests/:questId", authentificateUser, (req, res) => { +app.get("/quests/:id", authentificateUser, (req, res) => { //res.send("My one random quest of the day"); - const id = req.params.questId; + const { id } = req.params; try { const dailyQuest = quests.find((item) => item._id === id); @@ -253,7 +253,8 @@ app.get("user/:id/quests/history", (req, res) => { }); // TODO ---- MAIN PAGES ---- -// ---- USER ---- + +// TODO ---- USER ---- // FIXME ---- Smiley state of mood ---- >>>> only for auth users, returns sad/happy/delighted avatars app.get("/user/:id/state", (req, res) => { /* console.log("this is your mode"); */ @@ -261,7 +262,7 @@ app.get("/user/:id/state", (req, res) => { }); // FIXME Nice+ ---- User page (shows: current strike, settings, log out, delete user, bonus points, profile picture state, user library) >>>>> only for auth users -app.get("/profile/:userid", (req, res) => { +app.get("/profile/:id", (req, res) => { /* console.log("user info page"); */ res.send("User profle"); }); @@ -287,7 +288,7 @@ app.get("/friends", (req, res) => { }); // FIXME NICE+ ---- Find a friend bi ID page -app.get("/friends/:friendid", async (req, res) => { +app.get("/friends/:id", async (req, res) => { const friend = await User.findById(req.params.id); //search through users ids in database? res.json(friend); }); @@ -310,8 +311,26 @@ app.delete("/user/:id", (req, res) => { // TODO ---- QUESTS ---- // FIXME MUST ---- Delete one quest >>>>> only for authorised users for their list -app.delete("/user/:id/quests/:id", (req, res) => { - console.log("delete test"); +app.delete("/quests/:id", (req, res) => { + //console.log("delete test"); + const { id } = req.params; + + try { + const quest = quests.find((item) => item._id === id); + if (!quest) { + return res + .status(404) + .json({ error: `Couldn't find and delete quest with id ${id}` }); + } + + const allQuest = quests.filter((item) => item._id != id); + + quests = allQuest; + + res.json(quest); + } catch (err) { + res.status(400).json({ error: `Something went wrong, ${id} is not valid` }); + } }); // FIXME EXTRA ---- DELETE more than 1 quest at a time >>>>> only for authorised users for their list @@ -336,7 +355,7 @@ app.patch("/profile/:id/settings", (req, res) => { }); // FIXME NICE+ ---- Edit one quest >>>>> only for authorised users for their list -app.patch("/user/:id/quests/:id", (req, res) => { +app.patch("/quests/:id", (req, res) => { console.log("delete test"); }); From b0d0f2f315241cbeee7f4b2743390f4995a99f9a Mon Sep 17 00:00:00 2001 From: Julia Demianetc Date: Tue, 17 Feb 2026 16:58:54 +0100 Subject: [PATCH 6/7] updated delete route --- backend/package.json | 1 + backend/server.js | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4c8b5d1a4d..079c480380 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,6 +17,7 @@ "bcrypt-nodejs": "^0.0.3", "cors": "^2.8.5", "crypto": "^1.0.1", + "dotenv": "^17.3.1", "express": "^4.22.1", "express-list-endpoints": "^7.1.1", "mongodb": "^7.1.0", diff --git a/backend/server.js b/backend/server.js index 91e5d17b3a..1587044408 100644 --- a/backend/server.js +++ b/backend/server.js @@ -7,6 +7,7 @@ import { User } from "./schemas"; import quests from "./quests.json"; import bcrypt from "bcrypt-nodejs"; import { authentificateUser } from "./authMiddleware"; +import "dotenv/config"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; mongoose.connect(mongoUrl); @@ -18,7 +19,7 @@ const app = express(); app.use(cors()); app.use(express.json()); -//Middleware to handle error at service availability before running anything else +// ---- Middleware to handle error at service availability before running anything else app.use((req, res, next) => { if (mongoose.connection.readyState === 1) { next(); @@ -36,6 +37,7 @@ app.get("/", (req, res) => { message: "List of all endpoints", endpoints: endpoints, }); // FIXME delete res.json before prod! + console.log("OUR ENV VAR", process.env.OUR_VAR); }); // TODO ---- POST ENDPOINTS ---- @@ -311,23 +313,25 @@ app.delete("/user/:id", (req, res) => { // TODO ---- QUESTS ---- // FIXME MUST ---- Delete one quest >>>>> only for authorised users for their list -app.delete("/quests/:id", (req, res) => { +app.delete("/quests/:id", async (req, res) => { //console.log("delete test"); const { id } = req.params; + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(404).json({ + error: `Couldn't find the quest with id ${id}, check if it is valid.`, + }); + } + try { - const quest = quests.find((item) => item._id === id); + const quest = await Quest.findByIdAndDelete(id).exec(); if (!quest) { return res .status(404) .json({ error: `Couldn't find and delete quest with id ${id}` }); } - const allQuest = quests.filter((item) => item._id != id); - - quests = allQuest; - - res.json(quest); + res.status(200).json({ message: "Quest was successfully deleted" }); } catch (err) { res.status(400).json({ error: `Something went wrong, ${id} is not valid` }); } From 98bf0fd6cbe21a4d57446e28c63ef8240ab0d7ff Mon Sep 17 00:00:00 2001 From: Julia Demianetc Date: Tue, 17 Feb 2026 23:27:40 +0100 Subject: [PATCH 7/7] added new routes, updated prev routes --- backend/schemas.js | 2 + backend/server.js | 232 +++++++++++++++++++++++++++++++-------------- 2 files changed, 165 insertions(+), 69 deletions(-) diff --git a/backend/schemas.js b/backend/schemas.js index f0907e878b..bc2a6751d3 100644 --- a/backend/schemas.js +++ b/backend/schemas.js @@ -24,6 +24,8 @@ const questSchema = new mongoose.Schema( category: [ { type: String, + lowercase: true, + trim: true, }, ], diff --git a/backend/server.js b/backend/server.js index 1587044408..0d4152513a 100644 --- a/backend/server.js +++ b/backend/server.js @@ -9,16 +9,28 @@ import bcrypt from "bcrypt-nodejs"; import { authentificateUser } from "./authMiddleware"; import "dotenv/config"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; -mongoose.connect(mongoUrl); -mongoose.Promise = Promise; - const port = process.env.PORT || 8080; const app = express(); app.use(cors()); app.use(express.json()); +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; +mongoose.connect(mongoUrl); +mongoose.Promise = Promise; + +//seeding of DB, OBS! only if =true +if (process.env.RESET_FB) { + const seedDatabase = async () => { + await Quest.deleteMany(); //this will delete everything we have in database + + quests.forEach((quest) => { + new Quest(quest).save(); + }); + }; + seedDatabase(); +} + // ---- Middleware to handle error at service availability before running anything else app.use((req, res, next) => { if (mongoose.connection.readyState === 1) { @@ -28,7 +40,7 @@ app.use((req, res, next) => { } }); -// ---- All ENDPOINTS, temporarily ---- +// ---- All ENDPOINTS, temporary ---- app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -37,18 +49,32 @@ app.get("/", (req, res) => { message: "List of all endpoints", endpoints: endpoints, }); // FIXME delete res.json before prod! - console.log("OUR ENV VAR", process.env.OUR_VAR); + //console.log("OUR ENV VAR", process.env.OUR_VAR); }); // TODO ---- POST ENDPOINTS ---- // TODO ---- USER ---- -// FIXME update error handling and more MUST ---- Register new user ---- +// MUST ---- Register new user ---- app.post("/signup", async (req, res) => { try { const { name, email, password } = req.body; + if (!name || !email || !password) { + return res.status(400).json({ + success: false, + message: "All fields are required to sign up", + }); + } + + if (password.length < 8) { + return res.status(400).json({ + success: false, + message: "Password must be at least 8 characters long", + }); + } + //One-way encryption: const salt = bcrypt.genSaltSync(); const hashedPass = bcrypt.hashSync(password, salt); @@ -58,19 +84,35 @@ app.post("/signup", async (req, res) => { email, password: hashedPass, }); + await user.save(); res.status(201).json({ id: user._id, accessToken: user.accessToken }); } catch (err) { - res - .status(400) - .json({ message: "Could not create user", errors: err.errors }); + if (err.code === 11000) { + return res.status(400).json({ + success: false, + message: "User with this name or email already exists", + }); + } + res.status(400).json({ + success: false, + message: "Could not create user", + errors: err.errors, + }); } }); -// FIXME Update error handling / MUST ---- Login with existing user ---- +// MUST ---- Login with existing user ---- app.post("/login", async (req, res) => { const { email, password } = req.body; const user = await User.findOne({ email }); //retrieving from database by email, should be unique + + if (!email || !password) { + return res + .status(400) + .json({ success: false, message: "All fields are required to login" }); + } + try { if (user && bcrypt.compareSync(password, user.password)) { //Success @@ -79,10 +121,13 @@ app.post("/login", async (req, res) => { //Failed: //1.User doesn't exist //2.Password doesn't match - res.json({ message: "Smth went wrong, check your email and password" }); + res.status(401).json({ + success: false, + message: "Smth went wrong, check your email and password", + }); } } catch (err) { - res.status(400).json({ err: "Bad request" }); + res.status(500).json({ success: false, err: "Something went wrong" }); } }); @@ -114,12 +159,12 @@ app.post("/quests/library/add", (req, res) => { }); // FIXME MUST ---- Complete a quest >>>>> only for auth users -app.post("quests/:questid/complete", (req, res) => { +app.post("quests/:id/complete", (req, res) => { console.log("Task is done"); }); //FIXME NICE+ ---- User completes task too fast confirmation >>>>> only for auth users -app.post("quests/:questid/confirm-complete", (req, res) => { +app.post("quests/:id/confirm-complete", (req, res) => { console.log("Do not cheat, ok?"); }); @@ -133,13 +178,8 @@ app.post("quests/random/:sessionId/retry", (req, res) => { console.log("re-try"); }); -//FIXME NICE+ ---- User completes task too fast confirmation >>>>> only for auth users -app.post("quests/:questid/confirm-complete", (req, res) => { - console.log("Do not cheat, ok?"); -}); - // FIXME EXTRA ---- Add actual time >>>>> only for auth users -app.post("quests/:questid/add-time", (req, res) => { +app.post("quests/:id/add-time", (req, res) => { console.log("Add actual time"); }); @@ -147,12 +187,36 @@ app.post("quests/:questid/add-time", (req, res) => { app.post("/quests/skip"); // FIXME NICE+ ---- Repetitive quests >>>>> only for auth users -app.post("/quests/:questid/repeat"); +app.post("/quests/:id/repeat"); // ---- FRIENDS ---- -// FIXME MUST --- Give kudos >>>>> only for auth users -app.post("/friends/:postid/kudos", authentificateUser, (req, res) => { - console.log("Give kudos"); +// FIXME add auth MUST --- Give kudos >>>>> only for auth users +app.post("/friends/:postid/kudos", async (req, res) => { + //console.log("Give kudos"); + const update = { $inc: { kudos: 1 } }; + const options = { new: true, runValidators: true }; + + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(404).json({ success: false, response: "Id is invalid" }); + } + + try { + const addKudos = await Quest.findByIdAndUpdate(id, update, options); + + if (!addKudos) { + return res.status(404).json({ + success: false, + message: "Can't add kudos, entry is invalid or it was deleted", + }); + } + res.status(200).json(addKudos); + } catch (err) { + return res.status(500).json({ + success: false, + message: "Couldn't add kudos, try again", + error: err.errors, + }); + } }); // TODO ---- PUNISHMENTS ---- @@ -170,87 +234,118 @@ app.post("/punishment/lock", (req, res) => { // TODO ---- QUESTS ---- -// FIXME add error handling // MUST ---- Quests default library (returns default tasks, categories, est time), can filter on one category and time <= N +// FIXME ??? MUST ---- Quests default library (returns default tasks, categories, est time), can filter on one category and time <= N. Returns from hardcoded file app.get("/quests/library", (req, res) => { const { category, time } = req.query; - //console.log("category", category); - let filteredQuests = quests; //Test example: http://localhost:8080/quests/library/?category=cleaning&time=20 - if (category) { - filteredQuests = filteredQuests.filter((item) => { - return item.category.some((word) => { - return word.toLowerCase() === category.toLowerCase(); + try { + let filteredQuests = quests; + + if (category) { + filteredQuests = filteredQuests.filter((item) => { + return item.category.some((word) => { + return word.toLowerCase() === category.toLowerCase(); + }); }); - }); + } + + if (time) { + filteredQuests = filteredQuests.filter((item) => { + return item.timeNeed <= Number(time); + }); + } + return res + .status(200) + .json({ success: true, response: filteredQuests, message: "Success" }); + } catch (err) { + return res + .status(500) + .json({ success: false, response: [], message: err.errors }); } +}); +// FIXME ?? MUST ----- Returns all user's quests, can filter on category and time <= N) >>>>> only for auth users. +// Returns from database --------- +app.get("/quests/all", authentificateUser, async (req, res) => { + let { category, time } = req.query; + const query = { createdBy: req.user._id }; + + if (category) { + category = category.toLowerCase(); + query.category = category; + } if (time) { - filteredQuests = filteredQuests.filter((item) => { - return item.timeNeed <= Number(time); - }); + time = Number(time); + query.timeNeeded = { $lte: time }; } - res.json(filteredQuests); -}); - -// FIXME MUST --- User's quests (returns all tasks user saved to their list, both from library and user-created, with categories and est time; can filter on categories and time <= N) >>>>> only for auth users -//Currently: only checks build-in library -app.get("/user/:id/quests/", authentificateUser, async (req, res) => { - //const userQuests = await Quest.find().populate("createdBy"); - //res.send(userQuests); - const user = await User.findById(req.params.id); - const quests = await Quest.find({ - user: mongoose.Types.ObjectId.createFromHexString(user.id), - }); + try { - if (user) { - res.json(quests); - } else { - res.status(404).json({ error: "Oops, user not found" }); + const filteredQuests = await Quest.find(query).populate("createdBy"); + + if (!filteredQuests.length) { + return res.status(404).json({ + success: false, + response: [], + message: "Couldn't find any quests with these filters", + }); } + return res + .status(200) + .json({ success: true, response: filteredQuests, message: "Success" }); } catch (err) { - res.status(400).json({ error: "Invalid user ID" }); + return res + .status(500) + .json({ success: false, response: [], mesasage: err.errors }); } }); -// FIXME MUST ---- User's done quests /user/:id/quests/done/true +// FIXME add auth and error handling MUST ---- User's done quests /quests/done/true app.get("quests/done/:done", (req, res) => { const done = req.params.done; const questsDone = quests.filter((item) => item.done === done); res.json(questsDone); }); -// FIXME add randomizing, add looking through all database update error handling // MUST ---- User's daily random(!) quest >>>>> only for auth users // "/user/:userId/quests/:questId" -//NOW: only finds one from in-build library -app.get("/quests/:id", authentificateUser, (req, res) => { - //res.send("My one random quest of the day"); +// FIXME add randomizing, add looking through all users quests(added from library to users database) +// MUST ---- User's daily random(!) quest >>>>> only for auth users // "/user/:userId/quests/:questId" +//NOW: only finds one from general database +app.get("/quests/:id", authentificateUser, async (req, res) => { const { id } = req.params; try { - const dailyQuest = quests.find((item) => item._id === id); - + // const libraryQuests = quests.find((item) => item._id === id); + const dailyQuest = await Quest.findById(id); //from database if (!dailyQuest) { - return res.status(404).json({ error: `Quest with ${id} is not found` }); + return res.status(404).json({ + success: false, + respons: null, + error: `Quest with ${id} is not found`, + }); } - res.json(dailyQuest); + res.json({ success: true, response: dailyQuest }); } catch (err) { - res.status(400).json({ error: `Something went wrong, ${id} is not valid` }); + res.status(500).json({ + success: false, + response: null, + error: `Something went wrong, ${id} is not valid`, + }); } }); // FIXME MUST ---- User's Rewards Collection >>>>> only for auth users -app.get("user/:id/rewards", (req, res) => { +app.get("/rewards", (req, res) => { res.send("Your reward is here"); }); // FIXME MUST ---- Streaks >>>>> only for auth users -app.get("user/:id/streaks", (req, res) => { +app.get("/streaks", (req, res) => { console.log("Your streak"); }); // FIXME NICE+ ---- Quests history >>>>>> only for auth users -app.get("user/:id/quests/history", (req, res) => { +app.get("/quests/history", (req, res) => { console.log("Shows how much user have done before"); }); @@ -359,9 +454,8 @@ app.patch("/profile/:id/settings", (req, res) => { }); // FIXME NICE+ ---- Edit one quest >>>>> only for authorised users for their list -app.patch("/quests/:id", (req, res) => { - console.log("delete test"); -}); + +// ------ PATCH ENDPOINTS ----- // Start the server app.listen(port, () => {