diff --git a/src/controllers/course.controllers.ts b/src/controllers/course.controllers.ts new file mode 100644 index 0000000..44b4aed --- /dev/null +++ b/src/controllers/course.controllers.ts @@ -0,0 +1,117 @@ +import { Request, Response } from "express"; +import User from "../models/user.model"; +import Course from "../models/course.model"; +import Flow from "../models/flow.model"; + +export async function createCourse(req: Request, res: Response) { + const userId = req.user?._id; + const { title, description, flowsId } = req.body; + + try { + if (!userId) { + return res.status(400).send("userId is required"); + } + + if(!title) res.status(400).send("title is required"); + + + if(!description) res.status(400).send("description is required"); + + const user = await User.findById(userId); + if (!user) { + return res.status(404).send("User not found"); + } + + if (await Course.findOne({ title: title })) { + return res.status(400).send("Course already exists"); + } + /* IMPLEMENT THIS LATER + let flowsNotFound: string[] = []; + console.log(flowsId); + if(flowsId.length > 0){ + for (const flow of flowsId) { + const dbflow = await Flow.findOne({ _id: flow }); + if (!dbflow) { + flowsNotFound.push(flow); + } + } + } + + if (flowsNotFound.length > 0) { + return res.status(404).send("Flows " + flowsNotFound.join(", ") + " not found"); + } +*/ + const course = new Course({ + title: title, + description: description, + author: userId, + }); + + if (flowsId) for (const flow of flowsId) if (flow!=null) course.flows.push(flow); + + console.log(course); + await course.save(); + const courseRes = await Course.find({title: course.title}) + .populate("author") + .populate("flows"); + + return res.status(201).json(courseRes); + } catch (err) { + console.error(err); + res.status(500).send; + } +} + +export async function deleteCourse(req: Request, res: Response) { + const courseId = req.params.id; + const userId = req.user?._id; + try { + if (!userId) { + return res.status(400).send("userId is required"); + } + + const user = await User.findById(userId); + if (!user) { + return res.status(404).send("User not found"); + } + + if (!courseId) { + return res.status(404).send("courseId is required"); + } + + const dbcourse = await Course.findById(courseId); + if (!dbcourse) { + return res.status(404).send("Course not found"); + } + + if (dbcourse.author !== userId && userId != "admin") { + return res.status(403).send("You are not the author of this course"); + } + + await Course.deleteOne({ _id: courseId }); + + return res.status(204).send(); + } catch (err) { + res.status(500).send; + } +} + +export async function getCourses(req: Request, res: Response) { + try { + const q = req.query?.q?.toString(); + const me = req.query?.me?.toString(); + const query: any = q ? { title: { $regex: q, $options: "i" } } : {}; + + if (me) { + query.author = req.user?._id; + } + + const courses = await Course.find(query) + .populate("author") + .populate("flows"); + return res.json(courses); + } catch (err) { + console.error(err); + return res.status(500).send; + } +} diff --git a/src/controllers/execution.controller.ts b/src/controllers/execution.controller.ts index c545d96..8027fce 100644 --- a/src/controllers/execution.controller.ts +++ b/src/controllers/execution.controller.ts @@ -24,7 +24,7 @@ type GetNextExerciseV2Body = { type ProgressBody = { ctxId: string; - satisfiedConditions: string[]; + satisfiedConditions?: string[]; flowId?: string; authorId: string; }; @@ -191,6 +191,9 @@ export async function progressExecution( if (flow.author != authorId && authorId != "admin") res.status(400).send("You need to be the author to unlock the progress"); + if (!satisfiedConditions) + return res.status(404).send("Error satisfied conditions missing"); + if (satisfiedConditions.length === 0) return res.status(200).json(null); const algo = flow?.execution?.algo ?? "Random Execution"; @@ -213,6 +216,39 @@ export async function progressExecution( } } +export async function resetProgress( + req: Request<{}, any, ProgressBody>, + res: Response, + next: NextFunction, +) { + const { ctxId, satisfiedConditions, authorId } = req.body; + try { + const ctx = ctxs[ctxId]; + + if (!ctx) { + return res.status(400).json({ error: "Ctx not found!" }); + } + + const flow = await PolyglotFlowModel.findById(ctx.flowId).populate([ + "nodes", + "edges", + ]); + + if (!flow) return res.status(404).send(); + + if (flow.author != authorId && authorId != "admin") + res.status(400).send("You need to be the author to unlock the progress"); + + //delete context with ctxId==ctx + //idea: ctxs[ctxId] remove + // Object.entries(ctxs).splice() + + return res.status(200).send("Execution resetted correctly"); + } catch (err) { + next(err); + } +} + export async function getNextExercisev2( req: Request<{}, any, GetNextExerciseV2Body>, res: Response, diff --git a/src/models/course.model.ts b/src/models/course.model.ts new file mode 100644 index 0000000..dba96f4 --- /dev/null +++ b/src/models/course.model.ts @@ -0,0 +1,30 @@ +import mongoose, { Document } from "mongoose"; +import { v4 as uuidv4 } from "uuid"; +import validator from "validator"; + +export type CourseDocument = Document & { + title: string; + description: string; + author: string; + flows: string[]; +}; + +const courseSchema = new mongoose.Schema({ + _id: { + type: String, + required: true, + default: () => uuidv4(), + validate: { + validator: (id: string) => validator.isUUID(id), + message: "Invalid UUID-v4", + }, + }, + title: { type: String, required: true }, + description: { type: String, required: true }, + author: { type: String, required: true, ref: "User" }, + flows: [{ type: String, ref: "Flow" }], +}); + +const Course = mongoose.model("Course", courseSchema); + +export default Course; diff --git a/src/routes/course.routes.ts b/src/routes/course.routes.ts new file mode 100644 index 0000000..e4a0593 --- /dev/null +++ b/src/routes/course.routes.ts @@ -0,0 +1,15 @@ +import express from "express"; +import { checkAuth } from "../middlewares/auth.middleware"; +import * as CourseController from "../controllers/course.controllers"; + +const router = express.Router(); +// cambiare tutto con flow + +router.route("/:id").delete(checkAuth, CourseController.deleteCourse); +router + .route("/") + .post(checkAuth, CourseController.createCourse) + .get(checkAuth, CourseController.getCourses); + +// get enrolled courses +export default router; diff --git a/src/routes/execution.routes.ts b/src/routes/execution.routes.ts index f7969f1..997a5fa 100644 --- a/src/routes/execution.routes.ts +++ b/src/routes/execution.routes.ts @@ -11,6 +11,7 @@ router.post("/actual", ExecutionController.getActualNode); router.post("/next", ExecutionController.getNextExercisev2); router.post("/progressInfo", ExecutionController.getFlowCtxs); router.post("/progressAction", ExecutionController.progressExecution); +router.post("/resetProgress", ExecutionController.progressExecution); router.post("/cmd", ExecutionController.sendCommand); // router.post("/next", ExecutionController.getNextExercise) diff --git a/src/routes/index.ts b/src/routes/index.ts index 903ab60..f88cf80 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,5 +1,6 @@ import express from "express"; import flowRouter from "./flows.routes"; +import courseRouter from "./course.routes"; import executionRouter from "./execution.routes"; import userRouter from "./user.routes"; import searchRouter from "./search.routes"; @@ -10,6 +11,7 @@ import conceptRouter from "./concept.routes"; const router = express.Router(); router.use("/api/flows", flowRouter); +router.use("/api/course", courseRouter); router.use("/api/execution", executionRouter); router.use("/api/user", userRouter); router.use("/api/search", searchRouter);