diff --git a/api.js b/api.js index 749df6f..309b6c7 100644 --- a/api.js +++ b/api.js @@ -40,6 +40,7 @@ var galleryRouter = require('./routes/gallery/index'); var projectRouter = require('./routes/project/index'); var userRouter = require('./routes/user/index'); var uploadRouter = require('./routes/upload/index'); +const ArduinoRouter = require('./routes/arduinoExamples'); api.use('/tutorial', tutorialRouter); api.use('/share', shareRouter); @@ -47,6 +48,7 @@ api.use('/gallery', galleryRouter); api.use('/project', projectRouter); api.use('/user', userRouter); api.use('/upload', uploadRouter); +api.use('/arduino', ArduinoRouter); // catch 404 and forward to error handler api.use(function(req, res, next) { diff --git a/models/arduinoExamples.js b/models/arduinoExamples.js new file mode 100644 index 0000000..f398368 --- /dev/null +++ b/models/arduinoExamples.js @@ -0,0 +1,33 @@ +// jshint esversion: 6 +// jshint node: true +"use strict"; + +const mongoose = require('mongoose'); + +const ArduinoExamplesSchema = new mongoose.Schema({ + title: { + type: String, + required: true + }, + description: { + type: String, + required: true + }, + creator: { + type: String, + ref: 'User', + required: true + }, + board: { + type: String, + required: true, + }, + code: { + type: String + } +},{ + timestamps: true +}); + + +module.exports = mongoose.model('ArduinoExamples', ArduinoExamplesSchema); diff --git a/models/gallery.js b/models/gallery.js index b211788..64ca16d 100644 --- a/models/gallery.js +++ b/models/gallery.js @@ -18,9 +18,16 @@ const GallerySchema = new mongoose.Schema({ ref: 'User', required: true }, + board: { + type: String, + required: true, + }, xml: { type: String - } + }, + type: { + type: String, + }, },{ timestamps: true }); diff --git a/models/project.js b/models/project.js index 1d2af1e..067d8b8 100644 --- a/models/project.js +++ b/models/project.js @@ -19,6 +19,10 @@ const ProjectSchema = new mongoose.Schema({ ref: 'User', required: true }, + board: { + type: String, + required: true, + }, xml: { type: String } diff --git a/models/share.js b/models/share.js index 4f8eefa..3727fd8 100644 --- a/models/share.js +++ b/models/share.js @@ -9,6 +9,10 @@ const ShareSchema = new mongoose.Schema({ type: String, required: true }, + board: { + type: String, + required: true, + }, expiresAt: { type: Date, required: true, diff --git a/models/tutorial.js b/models/tutorial.js index 21c421c..0bec2e9 100644 --- a/models/tutorial.js +++ b/models/tutorial.js @@ -57,6 +57,15 @@ const TutorialSchema = new mongoose.Schema( type: Number, required: true, }, + board: { + type: String, + required: false, + }, + language: { + type: String, + enum: ["en", "de"], + default: "en", + }, steps: [ { type: StepSchema, diff --git a/package.json b/package.json index a444c19..2f26405 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-ardublockly-backend", - "version": "0.0.0", + "version": "1.0.0", "private": true, "scripts": { "start": "node ./bin/www", diff --git a/routes/arduinoExamples/deleteExample.js b/routes/arduinoExamples/deleteExample.js new file mode 100644 index 0000000..cb21ec9 --- /dev/null +++ b/routes/arduinoExamples/deleteExample.js @@ -0,0 +1,56 @@ +// jshint esversion: 8 +// jshint node: true +"use strict"; + +const express = require('express'); +const mongoose = require('mongoose'); + +const arduinoExamples = require('../../models/arduinoExamples'); + +/** + * @api {delete} /arduino/:exampleId Delete gallery + * @apiName deleteExample + * @apiDescription Delete a specific example. + * @apiGroup ArduinoExamples + * + * @apiHeader {String} Authorization allows to send a valid JSON Web Token along with this request with `Bearer` prefix. + * @apiHeaderExample {String} Authorization Header Example + * Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMTk5OTEwY2QxMDgyMjA3Y2Y1ZGM2ZiIsImlhdCI6MTU3ODg0NDEwOSwiZXhwIjoxNTc4ODUwMTA5fQ.D4NKx6uT3J329j7JrPst6p02d311u7AsXVCUEyvoiTo + * + * @apiParam {ObjectId} exampleId the ID of the gallery you are referring to + * + * @apiSuccess (Success 200) {String} message `Example deleted successfully.` + * + * @apiError (On error) {Object} 403 `{"message": No permission deleting the gallery project."}` + * @apiError (On error) {Object} 404 `{"message": Example not found."}` + * @apiError (On error) {Obejct} 500 Complications during querying the database. + */ +const deleteExample = async function(req, res){ + try{ + var result = await arduinoExamples.findById(req.params.exampleId); + var owner = req.user.email; + if(owner === result.creator || req.user.role === 'admin'){ + var example = await arduinoExamples.deleteOne({_id: req.params.exampleId}); + if(example && example.deletedCount > 0){ + return res.status(200).send({ + message: 'Arduino Example deleted successfully.', + }); + } + return res.status(404).send({ + message: 'Example not found.', + }); + } + else { + return res.status(403).send({ + message: 'No permission deleting the example.', + }); + } + } + catch(err){ + return res.status(500).send(err); + } +}; + +module.exports = { + deleteExample +}; diff --git a/routes/arduinoExamples/getExample.js b/routes/arduinoExamples/getExample.js new file mode 100644 index 0000000..c205a8a --- /dev/null +++ b/routes/arduinoExamples/getExample.js @@ -0,0 +1,53 @@ +// jshint esversion: 8 +// jshint node: true +"use strict"; + +const express = require('express'); +const mongoose = require('mongoose'); +const arduinoExamples = require('../../models/arduinoExamples'); + + +/** + * @api {get} /gallery/:galleryId Get gallery + * @apiName getGallery + * @apiDescription Get a specific gallery. + * @apiGroup Gallery + * + * @apiHeader {String} Authorization allows to send a valid JSON Web Token along with this request with `Bearer` prefix. + * @apiHeaderExample {String} Authorization Header Example + * Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMTk5OTEwY2QxMDgyMjA3Y2Y1ZGM2ZiIsImlhdCI6MTU3ODg0NDEwOSwiZXhwIjoxNTc4ODUwMTA5fQ.D4NKx6uT3J329j7JrPst6p02d311u7AsXVCUEyvoiTo + * + * @apiParam {ObjectId} galleryId the ID of the gallery you are referring to + * + * @apiSuccess (Success 200) {String} message `Gallery found successfully.` + * @apiSuccess (Success 200) {Object} gallery `{ + "_id": "5fd8a66cb40982332c400bc4", + "title": "flimsy-cougar", + "description": "Beschreibung", + "xml": "\n \n", + "creator": "em@il.de", + "createdAt": "2020-12-15T12:05:00.662Z", + "updatedAt": "2020-12-15T12:05:00.662Z", + "__v": 0 + }` + * + * @apiError (On error) {Obejct} 500 Complications during querying the database. + */ +const getExample = async function(req, res){ + try{ + var id = req.params.exampleId; + var result = await arduinoExamples.findById(id); + // TODO: .populate({path: 'creator', model: 'User', select: 'name'}) + return res.status(200).send({ + message: 'Example found successfully.', + example: result + }); + } + catch(err){ + return res.status(500).send(err); + } +}; + +module.exports = { + getExample +}; diff --git a/routes/arduinoExamples/getExamples.js b/routes/arduinoExamples/getExamples.js new file mode 100644 index 0000000..1bf5a05 --- /dev/null +++ b/routes/arduinoExamples/getExamples.js @@ -0,0 +1,51 @@ +// jshint esversion: 8 +// jshint node: true +"use strict"; + +const express = require('express'); +const mongoose = require('mongoose'); + +const arduinoExamples = require('../../models/arduinoExamples'); + +/** + * @api {get} /arduino/ Get Arduino Examples + * @apiName getGalleries + * @apiDescription Get all galleries. + * @apiGroup Gallery + * + * @apiHeader {String} Authorization allows to send a valid JSON Web Token along with this request with `Bearer` prefix. + * @apiHeaderExample {String} Authorization Header Example + * Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMTk5OTEwY2QxMDgyMjA3Y2Y1ZGM2ZiIsImlhdCI6MTU3ODg0NDEwOSwiZXhwIjoxNTc4ODUwMTA5fQ.D4NKx6uT3J329j7JrPst6p02d311u7AsXVCUEyvoiTo + * + * @apiSuccess (Success 200) {String} message `Galleries found successfully.` + * @apiSuccess (Success 200) {Object} galleries `[ + { + "_id": "5fd8a66cb40982332c400bc4", + "title": "flimsy-cougar", + "description": "Beschreibung", + "xml": "\n \n", + "creator": "em@il.de", + "createdAt": "2020-12-15T12:05:00.662Z", + "updatedAt": "2020-12-15T12:05:00.662Z", + "__v": 0 + } +]` + * + * @apiError (On error) {Obejct} 500 Complications during querying the database. + */ +const getArduinoExamples = async function(req, res){ + try{ + var result = await arduinoExamples.find({}); + return res.status(200).send({ + message: 'Arduino Examples found successfully.', + arduinoExamples: result + }); + } + catch(err){ + return res.status(500).send(err); + } +}; + +module.exports = { + getArduinoExamples +}; diff --git a/routes/arduinoExamples/index.js b/routes/arduinoExamples/index.js new file mode 100644 index 0000000..c807dcf --- /dev/null +++ b/routes/arduinoExamples/index.js @@ -0,0 +1,25 @@ +// jshint esversion: 8 +// jshint node: true +"use strict"; + +var express = require('express'); +var ArduinoRouter = express.Router(); + +const { userAuthorization } = require('../../helper/userAuthorization'); + + ArduinoRouter.route('/') + .post(userAuthorization, require('./postExample').postExample); + + ArduinoRouter.route('/:exampleId') + .put(userAuthorization, require('./putExample').putExample); + + ArduinoRouter.route('/:exampleId') + .delete(userAuthorization, require('./deleteExample').deleteExample); + + ArduinoRouter.route('/') + .get(require('./getExamples').getArduinoExamples); + + ArduinoRouter.route('/:exampleId') + .get(require('./getExample').getExample); + +module.exports = ArduinoRouter; diff --git a/routes/arduinoExamples/postExample.js b/routes/arduinoExamples/postExample.js new file mode 100644 index 0000000..df13e3c --- /dev/null +++ b/routes/arduinoExamples/postExample.js @@ -0,0 +1,77 @@ +// jshint esversion: 8 +// jshint node: true +"use strict"; + +const express = require('express'); +const mongoose = require('mongoose'); + +const arduinoExamples = require('../../models/arduinoExamples'); +const User = require('../../models/user'); + + +/** + * @api {post} /arduino Create Arduino Example + * @apiName postExample + * @apiDescription Create a Arduino Example. + * @apiGroup ArduinoExamples + * + * @apiHeader {String} Authorization allows to send a valid JSON Web Token along with this request with `Bearer` prefix. + * @apiHeaderExample {String} Authorization Header Example + * Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMTk5OTEwY2QxMDgyMjA3Y2Y1ZGM2ZiIsImlhdCI6MTU3ODg0NDEwOSwiZXhwIjoxNTc4ODUwMTA5fQ.D4NKx6uT3J329j7JrPst6p02d311u7AsXVCUEyvoiTo + * + * @apiParam {String} title name of the project + * @apiParam {String} description further information about the project + * @apiParam {String} xml XML-String of the blockly-content + * + * @apiSuccess (Success 201) {String} message `Gallery is successfully created.` + * @apiSuccess (Success 201) {Object} gallery `{ + "_id": "5fd8a66cb40982332c400bc4", + "title": "flimsy-cougar", + "description": "Beschreibung", + "xml": "\n \n", + "creator": "em@il.de", + "createdAt": "2020-12-15T12:05:00.662Z", + "updatedAt": "2020-12-15T12:05:00.662Z", + "__v": 0 + }` + * + * @apiError (On error) {Object} 403 `{"message": No permission creating the gallery project."}` + * @apiError (On error) {Obejct} 500 Complications during querying the database. + */ +const postExample = async function(req, res){ + // const {error} = projectValidation(req.body); + // if(error) return res.status(422).send({message: error.details[0].message}); + try{ + var user = await User.findOne({email: req.user.email}); + if(user.role !== 'user'){ + const body = { + _id: new mongoose.Types.ObjectId(), + title: req.body.title, + description: req.body.description, + code: req.body.code, + creator: req.user.email, + board: req.body.board, + type: req.body.type, + }; + const example = new arduinoExamples(body); + const savedExamples = await example.save(); + return res.status(201).send({ + message: 'Gallery is successfully created.', + arduinoExamples: savedExamples + }); + } + else { + return res.status(403).send({ + message: 'No permission creating the gallery project.', + }); + } + } + catch(err) { + console.log(err); + return res.status(500).send(err); + } +}; + +module.exports = { + postExample +}; diff --git a/routes/arduinoExamples/putExample.js b/routes/arduinoExamples/putExample.js new file mode 100644 index 0000000..1fc4786 --- /dev/null +++ b/routes/arduinoExamples/putExample.js @@ -0,0 +1,80 @@ +// jshint esversion: 8 +// jshint node: true +"use strict"; + +const express = require('express'); +const mongoose = require('mongoose'); +const arduinoExamples = require('../../models/arduinoExamples'); + +const User = require('../../models/user'); + +/** + * @api {put} /gallery/:galleryId Update gallery + * @apiName putGallery + * @apiDescription Update a specific gallery. + * @apiGroup Gallery + * + * @apiHeader {String} Authorization allows to send a valid JSON Web Token along with this request with `Bearer` prefix. + * @apiHeaderExample {String} Authorization Header Example + * Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlMTk5OTEwY2QxMDgyMjA3Y2Y1ZGM2ZiIsImlhdCI6MTU3ODg0NDEwOSwiZXhwIjoxNTc4ODUwMTA5fQ.D4NKx6uT3J329j7JrPst6p02d311u7AsXVCUEyvoiTo + * + * @apiParam {ObjectId} galleryId the ID of the gallery you are referring to + * @apiParam {String} [title] name of the project + * @apiParam {String} [description] further information about the project + * @apiParam {String} [xml] XML-String of the blockly-content + * + * @apiSuccess (Success 200) {String} message `Gallery is updated successfully.` + * @apiSuccess (Success 200) {Object} gallery `{ + "_id": "5fd8a66cb40982332c400bc4", + "title": "flimsy-cougar", + "description": "Beschreibung", + "xml": "\n \n", + "creator": "em@il.de", + "createdAt": "2020-12-15T12:05:00.662Z", + "updatedAt": "2020-12-15T12:05:00.662Z", + "__v": 0 + }` + * + * @apiError (On error) {Object} 400 `{"message": Gallery not found."}` + * @apiError (On error) {Object} 403 `{"message": No permission updating the gallery project."}` + * @apiError (On error) {Obejct} 500 Complications during querying the database. + */ +const putExample = async function(req, res){ + // const {error} = projectValidation(req.body); + // if(error) return res.status(422).send({message: error.details[0].message}); + try { + var oldExample = await arduinoExamples.findOne({_id: req.params.galleryId}); + if(oldExample){ + var user = await User.findOne({email: req.user.email}); + var owner = req.user.email; + if(owner === oldExample.creator || user.role === 'admin'){ + var updatedExample = {}; + updatedExample.title = req.body.title || oldExample.title; + updatedExample.description = req.body.description || oldExample.description; + updatedExample.code = req.body.code || oldExample.code; + var example = await arduinoExamples.findOneAndUpdate({_id: oldExample._id}, updatedExample, {upsert: true, new: true}); + return res.status(200).send({ + message: 'Gallery is updated successfully.', + example: example + }); + } + else { + return res.status(403).send({ + message: 'No permission updating the gallery project.', + }); + } + } + else { + return res.status(400).send({ + message: 'Gallery not found.', + }); + } + } + catch(err){ + return res.status(500).send(err); + } +}; + +module.exports = { + putExample +}; diff --git a/routes/gallery/deleteGallery.js b/routes/gallery/deleteGallery.js index a1e49fa..89bea00 100644 --- a/routes/gallery/deleteGallery.js +++ b/routes/gallery/deleteGallery.js @@ -29,7 +29,7 @@ const deleteGallery = async function(req, res){ try{ var result = await Gallery.findById(req.params.galleryId); var owner = req.user.email; - if(owner === result.creator){ + if(owner === result.creator || req.user.role === 'admin'){ var gallery = await Gallery.deleteOne({_id: req.params.galleryId}); if(gallery && gallery.deletedCount > 0){ return res.status(200).send({ diff --git a/routes/gallery/postGallery.js b/routes/gallery/postGallery.js index 8b00c78..35a068b 100644 --- a/routes/gallery/postGallery.js +++ b/routes/gallery/postGallery.js @@ -50,6 +50,8 @@ const postGallery = async function(req, res){ description: req.body.description, xml: req.body.xml, creator: req.user.email, + board: req.body.board, + type: req.body.type, }; const gallery = new Gallery(body); const savedGallery = await gallery.save(); diff --git a/routes/gallery/putGallery.js b/routes/gallery/putGallery.js index e758d5f..4b16018 100644 --- a/routes/gallery/putGallery.js +++ b/routes/gallery/putGallery.js @@ -47,7 +47,7 @@ const putGallery = async function(req, res){ if(oldGallery){ var user = await User.findOne({email: req.user.email}); var owner = req.user.email; - if(owner === oldGallery.creator){ + if(owner === oldGallery.creator || user.role === 'admin'){ var updatedGallery = {}; updatedGallery.title = req.body.title || oldGallery.title; updatedGallery.description = req.body.description || oldGallery.description; diff --git a/routes/project/postProject.js b/routes/project/postProject.js index 73fb021..d4b74f5 100644 --- a/routes/project/postProject.js +++ b/routes/project/postProject.js @@ -42,6 +42,7 @@ const postProject = async function(req, res){ title: req.body.title, xml: req.body.xml, creator: req.user.email, + board: req.body.board }; const project = new Project(body); const savedProject = await project.save(); diff --git a/routes/share/postShare.js b/routes/share/postShare.js index 55e04bb..3696cd4 100644 --- a/routes/share/postShare.js +++ b/routes/share/postShare.js @@ -56,6 +56,7 @@ const postShare = async function(req, res){ const body = { _id: req.body.projectId ? req.body.projectId : new mongoose.Types.ObjectId(), title: req.body.title, + board: req.body.board, expiresAt: moment.utc().add(Number(process.env.SHARE_EXPIRES_IN),'seconds').toDate(), }; if(req.body.xml){ body.xml = req.body.xml; } diff --git a/routes/tutorial/postTutorial.js b/routes/tutorial/postTutorial.js index 2bb6916..4748436 100644 --- a/routes/tutorial/postTutorial.js +++ b/routes/tutorial/postTutorial.js @@ -68,6 +68,7 @@ const postTutorial = async function (req, res) { difficulty: req.body.difficulty, public: req.body.public, review: req.body.review, + board: req.body.board, steps: req.body.steps, }; // storing existing images in mongoDB