├── .DS_Store ├── .gitignore ├── controllers ├── auth.controller.js ├── booking.controller.js ├── movie.controller.js ├── payment.controller.js ├── show.controller.js ├── theatre.controller.js └── user.controller.js ├── index.js ├── middlewares ├── auth.middlewares.js ├── booking.middlewares.js ├── movie.middlewares.js ├── payment.middlewares.js ├── show.middlewares.js ├── theatre.middleware.js └── user.middlewares.js ├── models ├── booking.model.js ├── movie.model.js ├── payment.model.js ├── show.model.js ├── theatre.model.js └── user.model.js ├── package-lock.json ├── package.json ├── routes ├── auth.routes.js ├── booking.routes.js ├── movie.routes.js ├── payment.routes.js ├── show.routes.js ├── theatre.routes.js └── user.routes.js ├── services ├── booking.services.js ├── email.service.js ├── movie.service.js ├── payment.service.js ├── show.service.js ├── theatre.service.js └── user.service.js └── utils ├── constants.js └── responsebody.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singhsanket143/Movie_Booking_API_Node/7b27264d4678639488f4c0a6aedfd97cc8be5732/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules/ -------------------------------------------------------------------------------- /controllers/auth.controller.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | const userService = require('../services/user.service'); 4 | const { successResponseBody, errorResponseBody } = require('../utils/responsebody'); 5 | 6 | const signup = async (req, res) => { 7 | try { 8 | const response = await userService.createUser(req.body); 9 | successResponseBody.data = response; 10 | successResponseBody.message = "Successfully registered a user"; 11 | return res.status(201).json(successResponseBody); 12 | } catch (error) { 13 | if(error.err) { 14 | errorResponseBody.err = error.err; 15 | return res.status(error.code).json(errorResponseBody); 16 | } 17 | errorResponseBody.err = error; 18 | return res.status(500).json(errorResponseBody); 19 | } 20 | } 21 | 22 | const signin = async (req, res) => { 23 | try { 24 | const user = await userService.getUserByEmail(req.body.email); 25 | const isValidPassword = await user.isValidPassword(req.body.password); 26 | if(!isValidPassword) { 27 | throw {err: 'Invalid password for the given email', code: 401}; 28 | } 29 | const token = jwt.sign( 30 | {id: user.id, email: user.email}, 31 | process.env.AUTH_KEY, 32 | {expiresIn: '1h'} 33 | ); 34 | 35 | successResponseBody.message = "Successfully logged in"; 36 | successResponseBody.data = { 37 | email: user.email, 38 | role: user.userRole, 39 | status: user.userStatus, 40 | token: token 41 | }; 42 | 43 | return res.status(200).json(successResponseBody); 44 | } catch (error) { 45 | if(error.err) { 46 | errorResponseBody.err = error.err; 47 | return res.status(error.code).json(errorResponseBody); 48 | } 49 | console.log(error); 50 | errorResponseBody.err = error; 51 | return res.status(500).json(errorResponseBody); 52 | } 53 | } 54 | 55 | const resetPassword = async (req, res) => { 56 | try { 57 | const user = await userService.getUserById(req.user); 58 | const isOldPasswordCorrect = await user.isValidPassword(req.body.oldPassword); 59 | if(!isOldPasswordCorrect) { 60 | throw {err: 'Invalid old password, please write the correct old password', code: 403}; 61 | } 62 | user.password = req.body.newPassword; 63 | await user.save(); 64 | successResponseBody.data = user; 65 | successResponseBody.message = 'Successfully updated the password for the given user'; 66 | return res.status(200).json(successResponseBody); 67 | } catch (error) { 68 | if(error.err) { 69 | errorResponseBody.err = error.err; 70 | return res.status(error.code).json(errorResponseBody); 71 | } 72 | errorResponseBody.err = error; 73 | return res.status(500).json(errorResponseBody); 74 | } 75 | } 76 | 77 | module.exports = { 78 | signup, 79 | signin, 80 | resetPassword 81 | } -------------------------------------------------------------------------------- /controllers/booking.controller.js: -------------------------------------------------------------------------------- 1 | const { successResponseBody, errorResponseBody } = require('../utils/responsebody'); 2 | const bookingService = require('../services/booking.services'); 3 | const { STATUS } = require('../utils/constants'); 4 | 5 | const create = async (req, res) => { 6 | try { 7 | let userId = req.user; 8 | const response = await bookingService.createBooking({...req.body, userId: userId}); 9 | successResponseBody.message = "Successfully created a booking"; 10 | successResponseBody.data = response; 11 | return res.status(STATUS.CREATED).json(successResponseBody); 12 | } catch(error) { 13 | if(error.err) { 14 | errorResponseBody.err = error.err; 15 | return res.status(error.code).json(errorResponseBody); 16 | } 17 | errorResponseBody.err = error; 18 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 19 | } 20 | } 21 | 22 | const update = async (req, res) => { 23 | try { 24 | const response = await bookingService.updateBooking(req.body, req.params.id); 25 | successResponseBody.data = response; 26 | successResponseBody.message = "Successfully updated the booking"; 27 | return res.status(STATUS.OK).json(successResponseBody); 28 | } catch (error) { 29 | if(error.err) { 30 | errorResponseBody.err = error.err; 31 | return res.status(error.code).json(errorResponseBody); 32 | } 33 | console.log(error); 34 | errorResponseBody.err = error; 35 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 36 | } 37 | } 38 | 39 | const getBookings = async (req, res, next) => { 40 | try { 41 | const response = await bookingService.getBookings({userId: req.user}); 42 | successResponseBody.data = response; 43 | successResponseBody.message = "Successfully fetched the bookings"; 44 | return res.status(STATUS.OK).json(successResponseBody); 45 | } catch (error) { 46 | errorResponseBody.err = error; 47 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 48 | } 49 | } 50 | 51 | const getAllBookings = async (req, res, next) => { 52 | try { 53 | const response = await bookingService.getAllBookings(); 54 | successResponseBody.data = response; 55 | successResponseBody.message = "Successfully fetched the bookings"; 56 | return res.status(STATUS.OK).json(successResponseBody); 57 | } catch (error) { 58 | errorResponseBody.err = error; 59 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 60 | } 61 | } 62 | 63 | const getBookingById = async (req, res, next) => { 64 | try { 65 | const response = await bookingService.getBookingById(req.params.id, req.user); 66 | successResponseBody.data = response; 67 | successResponseBody.message = "Successfully fetched the booking"; 68 | return res.status(STATUS.OK).json(successResponseBody); 69 | } catch (error) { 70 | if(error.err) { 71 | errorResponseBody.err = error.err; 72 | return res.status(error.code).json(errorResponseBody); 73 | } 74 | errorResponseBody.err = error; 75 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 76 | } 77 | } 78 | 79 | 80 | module.exports = { 81 | create, 82 | update, 83 | getBookings, 84 | getAllBookings, 85 | getBookingById 86 | } -------------------------------------------------------------------------------- /controllers/movie.controller.js: -------------------------------------------------------------------------------- 1 | const Movie = require('../models/movie.model'); 2 | const movieService = require('../services/movie.service'); 3 | const { successResponseBody, errorResponseBody} = require('../utils/responsebody'); 4 | const { STATUS } = require('../utils/constants'); 5 | /** 6 | * Controller function to create a new movie 7 | * @returns movie created 8 | */ 9 | 10 | const createMovie = async (req, res) => { 11 | try { 12 | const response = await movieService.createMovie(req.body); 13 | successResponseBody.data = response; 14 | successResponseBody.message = "Successfully created the movie"; 15 | 16 | return res.status(STATUS.CREATED).json(successResponseBody); 17 | } catch (error) { 18 | if(error.err) { 19 | errorResponseBody.err = error.err; 20 | return res.status(error.code).json(errorResponseBody); 21 | } 22 | errorResponseBody.err = error; 23 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 24 | } 25 | }; 26 | 27 | const deleteMovie = async (req, res) => { 28 | try { 29 | const response = await movieService.deleteMovie(req.params.id); 30 | successResponseBody.data = response; 31 | successResponseBody.message = "Successfully deleted the movie"; 32 | return res.status(STATUS.OK).json(successResponseBody); 33 | } catch (error) { 34 | if(error.err) { 35 | errorResponseBody.err = error.err; 36 | return res.status(error.code).json(errorResponseBody); 37 | } 38 | errorResponseBody.err = error; 39 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 40 | } 41 | } 42 | 43 | const getMovie = async (req, res) => { 44 | try { 45 | const response = await movieService.getMoviById(req.params.id); 46 | successResponseBody.data = response; 47 | return res.status(STATUS.OK).json(successResponseBody); 48 | 49 | } catch (error) { 50 | if(error.err) { 51 | errorResponseBody.err = error.err; 52 | return res.status(error.code).json(errorResponseBody); 53 | } 54 | errorResponseBody.err = error; 55 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 56 | } 57 | } 58 | 59 | const updateMovie = async (req, res) => { 60 | try { 61 | const response = await movieService.updateMovie(req.params.id, req.body); 62 | successResponseBody.data = response; 63 | return res.status(STATUS.OK).json(successResponseBody); 64 | } catch (error) { 65 | if(error.err) { 66 | errorResponseBody.err = error.err; 67 | return res.status(error.code).json(errorResponseBody); 68 | } 69 | errorResponseBody.err = error; 70 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 71 | } 72 | } 73 | 74 | const getMovies = async (req, res) => { 75 | try { 76 | const response = await movieService.fetchMovies(req.query); 77 | successResponseBody.data = response; 78 | return res.status(STATUS.OK).json(successResponseBody); 79 | } catch (error) { 80 | if(error.err) { 81 | errorResponseBody.err = error.err; 82 | return res.status(error.code).json(errorResponseBody); 83 | } 84 | errorResponseBody.err = error; 85 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 86 | } 87 | } 88 | 89 | module.exports = { 90 | createMovie, 91 | deleteMovie, 92 | getMovie, 93 | updateMovie, 94 | getMovies 95 | } -------------------------------------------------------------------------------- /controllers/payment.controller.js: -------------------------------------------------------------------------------- 1 | const paymentService = require('../services/payment.service'); 2 | const { BOOKING_STATUS, STATUS } = require('../utils/constants'); 3 | const { errorResponseBody, successResponseBody } = require('../utils/responsebody'); 4 | const User = require('../models/user.model'); 5 | const Movie = require('../models/movie.model'); 6 | const Theatre = require('../models/theatre.model'); 7 | const sendMail = require('../services/email.service'); 8 | 9 | const create = async (req, res) => { 10 | try { 11 | const response = await paymentService.createPayment(req.body); 12 | if(response.status == BOOKING_STATUS.expired) { 13 | errorResponseBody.err = 'The payment took more than 5 minutes to get processed, hence you booking got expired, please try again'; 14 | errorResponseBody.data = response; 15 | return res.status(STATUS.GONE).json(errorResponseBody); 16 | } 17 | if(response.status == BOOKING_STATUS.cancelled) { 18 | errorResponseBody.err = 'The payment failed due to some reason, booking was not successfull, please try again'; 19 | errorResponseBody.data = response; 20 | return res.status(STATUS.PAYMENT_REQUIRED).json(errorResponseBody); 21 | } 22 | const user = await User.findById(response.userId); 23 | const movie = await Movie.findById(response.movieId); 24 | const theatre = await Theatre.findById(response.theatreId); 25 | successResponseBody.data = response; 26 | successResponseBody.message = 'Booking completed successfully'; 27 | console.log(response, process.env.NOTI_SERVICE); 28 | // sendMail( 29 | // 'Your booking is Successfull', 30 | // response.userId, 31 | // `Your booking for ${movie.name} in ${theatre.name} for ${response.noOfSeats} seats on ${response.timing} is successfull. Your booking id is ${response.id}` 32 | // ); 33 | 34 | return res.status(STATUS.OK).json(successResponseBody); 35 | } catch (error) { 36 | if(error.err) { 37 | errorResponseBody.err = error.err; 38 | return res.status(error.code).json(errorResponseBody); 39 | } 40 | errorResponseBody.err = error; 41 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 42 | } 43 | } 44 | 45 | const getPaymentDetailsById = async (req, res) => { 46 | try { 47 | const response = await paymentService.getPaymentById(req.params.id); 48 | successResponseBody.data = response; 49 | successResponseBody.message = "Successfully fetched the booking and payment details"; 50 | return res.status(STATUS.OK).json(successResponseBody); 51 | } catch (error) { 52 | if(error.err) { 53 | errorResponseBody.err = error.err; 54 | return res.status(error.code).json(errorResponseBody); 55 | } 56 | errorResponseBody.err = error; 57 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 58 | } 59 | } 60 | 61 | const getAllPayments = async (req, res) => { 62 | try { 63 | const response = await paymentService.getAllPayments(req.user); 64 | successResponseBody.data = response; 65 | successResponseBody.message = "Successfully fetched all the payments"; 66 | return res.status(STATUS.OK).json(successResponseBody); 67 | } catch (error) { 68 | errorResponseBody.err = error; 69 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 70 | } 71 | } 72 | 73 | module.exports = { 74 | create, 75 | getPaymentDetailsById, 76 | getAllPayments 77 | } -------------------------------------------------------------------------------- /controllers/show.controller.js: -------------------------------------------------------------------------------- 1 | const showService = require('../services/show.service'); 2 | const { successResponseBody, errorResponseBody } = require('../utils/responsebody'); 3 | const { STATUS } = require('../utils/constants'); 4 | const create = async (req, res) => { 5 | try { 6 | const response = await showService.createShow(req.body); 7 | successResponseBody.message = "Successfully created the show"; 8 | successResponseBody.data = response; 9 | return res.status(STATUS.CREATED).json(successResponseBody); 10 | } catch (error) { 11 | if(error.err) { 12 | errorResponseBody.err = error.err; 13 | return res.status(error.code).json(errorResponseBody); 14 | } 15 | errorResponseBody.err = error; 16 | return res.status(STATUS.OK).json(errorResponseBody); 17 | } 18 | } 19 | 20 | const getShows = async (req, res) => { 21 | try { 22 | const response = await showService.getShows(req.query); 23 | successResponseBody.message = "Successfully fetched the movie shows"; 24 | successResponseBody.data = response; 25 | return res.status(STATUS.OK).json(successResponseBody); 26 | } catch (error) { 27 | if(error.err) { 28 | errorResponseBody.err = error.err; 29 | return res.status(error.code).json(errorResponseBody); 30 | } 31 | errorResponseBody.err = error; 32 | return res.status(STATUS.INTERNAL_SERVER_ERROR); 33 | } 34 | } 35 | 36 | const destroy = async (req, res) => { 37 | try { 38 | const response = await showService.deleteShow(req.params.id); 39 | successResponseBody.data = response; 40 | successResponseBody.message = "Successfully deleted the show"; 41 | return res.status(STATUS.OK).json(successResponseBody); 42 | } catch (error) { 43 | if(error.err) { 44 | errorResponseBody.err = error.err; 45 | return res.status(error.code).json(errorResponseBody); 46 | } 47 | errorResponseBody.err = error; 48 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 49 | } 50 | } 51 | 52 | const update = async (req, res) => { 53 | try { 54 | const response = await showService.updateShow(req.params.id, req.body); 55 | successResponseBody.data = response; 56 | successResponseBody.message = "Successfully updated the show"; 57 | return res.status(STATUS.OK).json(successResponseBody); 58 | } catch (error) { 59 | if(error.err) { 60 | errorResponseBody.err = error.err; 61 | return res.status(error.code).json(errorResponseBody); 62 | } 63 | console.log(error); 64 | errorResponseBody.err = error; 65 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 66 | } 67 | } 68 | 69 | module.exports = { 70 | create, 71 | getShows, 72 | destroy, 73 | update 74 | } -------------------------------------------------------------------------------- /controllers/theatre.controller.js: -------------------------------------------------------------------------------- 1 | const theatreService = require('../services/theatre.service'); 2 | const { successResponseBody, errorResponseBody} = require('../utils/responsebody'); 3 | const { STATUS } = require('../utils/constants'); 4 | const sendMail = require('../services/email.service'); 5 | 6 | 7 | const create = async (req, res) => { 8 | try { 9 | const response = await theatreService.createTheatre({...req.body, owner: req.user}); 10 | successResponseBody.data = response; 11 | successResponseBody.message = "Successfully created the theatre" 12 | sendMail( 13 | 'Successfully created a theatre', 14 | req.user, 15 | 'You have successfully created a new theatre' 16 | ) 17 | return res.status(STATUS.CREATED).json(successResponseBody); 18 | } catch (error) { 19 | if(error.err) { 20 | errorResponseBody.err = error.err; 21 | return res.status(error.code).json(errorResponseBody); 22 | } 23 | errorResponseBody.err = error; 24 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 25 | } 26 | } 27 | 28 | const destroy = async (req, res) => { 29 | try { 30 | const response = await theatreService.deleteTheatre(req.params.id); 31 | successResponseBody.data = response; 32 | successResponseBody.message = "Successfully deleted the given theatre"; 33 | return res.status(STATUS.OK).json(successResponseBody); 34 | } catch (error) { 35 | if(error.err) { 36 | errorResponseBody.err = error.err; 37 | return res.status(error.code).json(errorResponseBody); 38 | } 39 | errorResponseBody.err = error; 40 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 41 | } 42 | } 43 | 44 | const getTheatre = async (req, res) => { 45 | try { 46 | const response = await theatreService.getTheatre(req.params.id); 47 | successResponseBody.data = response; 48 | successResponseBody.message = "Successfully fetched the data of the theatre"; 49 | return res.status(STATUS.OK).json(successResponseBody); 50 | } catch (error) { 51 | if(error.err) { 52 | errorResponseBody.err = error.err; 53 | return res.status(error.code).json(errorResponseBody); 54 | } 55 | errorResponseBody.err = error; 56 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 57 | } 58 | } 59 | 60 | const getTheatres = async (req, res) => { 61 | try { 62 | const response = await theatreService.getAllTheatres(req.query); 63 | successResponseBody.data = response; 64 | successResponseBody.message = "Successfully fetched all the theatres"; 65 | return res.status(STATUS.OK).json(successResponseBody); 66 | } catch (error) { 67 | errorResponseBody.err = error; 68 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 69 | } 70 | } 71 | 72 | const update = async (req, res) => { 73 | try { 74 | const response = await theatreService.updateTheatre(req.params.id, req.body); 75 | successResponseBody.data = response; 76 | successResponseBody.message = "Successfully updated the theatre"; 77 | return res.status(STATUS.OK).json(successResponseBody); 78 | } catch (error) { 79 | if(error.err) { 80 | errorResponseBody.err = error.err; 81 | return res.status(error.code).json(errorResponseBody); 82 | } 83 | errorResponseBody.err = error; 84 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 85 | } 86 | } 87 | 88 | const updateMovies = async (req, res) => { 89 | try { 90 | const response = await theatreService.updateMoviesInTheatres( 91 | req.params.id, 92 | req.body.movieIds, 93 | req.body.insert 94 | ); 95 | successResponseBody.data = response; 96 | successResponseBody.message = "Successfully updated movies in the theatre"; 97 | return res.status(STATUS.OK).json(successResponseBody); 98 | } catch (error) { 99 | if(error.err) { 100 | errorResponseBody.err = error.err; 101 | return res.status(error.code).json(errorResponseBody); 102 | } 103 | errorResponseBody.err = error; 104 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 105 | } 106 | } 107 | 108 | const getMovies = async (req, res) => { 109 | try { 110 | const response = await theatreService.getMoviesInATheatre(req.params.id); 111 | successResponseBody.data = response; 112 | successResponseBody.message = "Successfully fetched the movies for the theatre"; 113 | return res.status(STATUS.OK).json(successResponseBody); 114 | } catch (error) { 115 | if(error.err) { 116 | errorResponseBody.err = error.err; 117 | return res.status(error.code).json(errorResponseBody); 118 | } 119 | errorResponseBody.err = error; 120 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 121 | } 122 | } 123 | 124 | const checkMovie = async (req, res) => { 125 | try { 126 | const response = await theatreService.checkMovieInATheatre(req.params.theatreId, req.params.movieId); 127 | successResponseBody.data = response; 128 | successResponseBody.message = "Successfully checked if movie is present in the theatre"; 129 | return res.status(STATUS.OK).json(successResponseBody); 130 | } catch (error) { 131 | if(error.err) { 132 | errorResponseBody.err = error.err; 133 | return res.status(error.code).json(errorResponseBody); 134 | } 135 | errorResponseBody.err = error; 136 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 137 | } 138 | } 139 | 140 | module.exports = { 141 | create, 142 | destroy, 143 | getTheatre, 144 | getTheatres, 145 | update, 146 | updateMovies, 147 | getMovies, 148 | checkMovie 149 | } -------------------------------------------------------------------------------- /controllers/user.controller.js: -------------------------------------------------------------------------------- 1 | const userService = require('../services/user.service'); 2 | const { errorResponseBody, successResponseBody } = require('../utils/responsebody'); 3 | const { STATUS } = require('../utils/constants'); 4 | 5 | const update = async (req, res) => { 6 | try { 7 | const response = await userService.updateUserRoleOrStatus(req.body, req.params.id); 8 | 9 | successResponseBody.data = response; 10 | successResponseBody.message = 'Successfully updated the user'; 11 | return res.status(STATUS.OK).json(successResponseBody); 12 | } catch (error) { 13 | if(error.err) { 14 | errorResponseBody.err = error.err; 15 | return res.status(error.code).json(errorResponseBody); 16 | } 17 | errorResponseBody.err = error; 18 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 19 | } 20 | } 21 | 22 | module.exports = { 23 | update 24 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); // cmnt 2 | const bodyParser = require('body-parser'); 3 | const env = require('dotenv'); 4 | const cors = require('cors') 5 | const mongoose = require('mongoose'); 6 | 7 | const MovieRoutes = require('./routes/movie.routes'); 8 | const theatreRoutes = require('./routes/theatre.routes'); 9 | const authRoutes = require('./routes/auth.routes'); 10 | const userRoutes = require('./routes/user.routes'); 11 | const bookingRoutes = require('./routes/booking.routes'); 12 | const showRoutes = require('./routes/show.routes'); 13 | const paymentRoutes = require('./routes/payment.routes'); 14 | 15 | env.config(); 16 | const app = express(); // express app object 17 | 18 | app.use(cors()) 19 | 20 | // configuring body parser 21 | app.use(bodyParser.urlencoded({extended: true})); 22 | app.use(bodyParser.json()); 23 | 24 | mongoose.set('debug', true); 25 | 26 | MovieRoutes(app); // invoking movie routes 27 | theatreRoutes(app); // invoking theatre routes 28 | authRoutes(app); // invoking auth routes 29 | userRoutes(app); // invoking user routes 30 | bookingRoutes(app); // invoking booking routes 31 | showRoutes(app); // invoking show routes 32 | paymentRoutes(app); // invoking payment routes 33 | 34 | app.get('/', (req, res) => { 35 | res.send('Home'); 36 | }) 37 | 38 | app.listen(process.env.PORT, async () => { 39 | // this callback gets execcuted, once we successfully start the server on the given port 40 | console.log(`Server started on Port ${process.env.PORT} !!`); 41 | 42 | try { 43 | if(process.env.NODE_ENV == 'production') { 44 | await mongoose.connect(process.env.PROD_DB_URL); // connected to the mongo server 45 | } else { 46 | await mongoose.connect(process.env.DB_URL); // connected to the mongo server 47 | } 48 | 49 | console.log("Successfully connected to mongo"); 50 | } catch (err) { 51 | console.log("Not able to connect mongo", err); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /middlewares/auth.middlewares.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | const { errorResponseBody } = require('../utils/responsebody'); 4 | const userService = require('../services/user.service'); 5 | const { USER_ROLE, STATUS } = require('../utils/constants'); 6 | 7 | /** 8 | * validator for user signup 9 | * @param req -> http request object 10 | * @param res -> http response object 11 | * @param next -> next middleware 12 | */ 13 | const validateSignupRequest = async (req, res, next) => { 14 | // validate name of the user 15 | if(!req.body.name) { 16 | errorResponseBody.err = "Name of the user not present in the request"; 17 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 18 | } 19 | 20 | // validate email of the user 21 | if(!req.body.email) { 22 | errorResponseBody.err = "Email of the user not present in the request"; 23 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 24 | } 25 | 26 | // validate password present of the user 27 | if(!req.body.password) { 28 | errorResponseBody.err = "Password of the user not present in the request"; 29 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 30 | } 31 | 32 | // request is valid 33 | next(); 34 | } 35 | 36 | /** 37 | * validator for user signin 38 | * @param req -> http request object 39 | * @param res -> http response object 40 | * @param next -> next middleware 41 | */ 42 | const validateSigninRequest = async (req, res, next) => { 43 | // validate user email presence 44 | if(!req.body.email) { 45 | errorResponseBody.err = "No email provided for sign in"; 46 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 47 | } 48 | 49 | // validate user password presence 50 | if(!req.body.password) { 51 | errorResponseBody.err = "No password provided for sign in"; 52 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 53 | } 54 | 55 | // request is valid 56 | next(); 57 | } 58 | 59 | const isAuthenticated = async (req, res, next) => { 60 | try { 61 | const token = req.headers["x-access-token"]; 62 | if(!token) { 63 | errorResponseBody.err = "No token provided"; 64 | return res.status(STATUS.FORBIDDEN).json(errorResponseBody); 65 | } 66 | const response = jwt.verify(token, process.env.AUTH_KEY); 67 | if(!response) { 68 | errorResponseBody.err = "Token not verified"; 69 | return res.status(STATUS.UNAUTHORISED).json(errorResponseBody); 70 | } 71 | const user = await userService.getUserById(response.id); 72 | req.user = user.id; 73 | next(); 74 | } catch (error) { 75 | if(error.name == "JsonWebTokenError") { 76 | errorResponseBody.err = error.message; 77 | return res.status(STATUS.UNAUTHORISED).json(errorResponseBody); 78 | } 79 | if(error.code == STATUS.NOT_FOUND) { 80 | errorResponseBody.err = "User doesn't exist" 81 | return res.status(error.code).json(errorResponseBody); 82 | } 83 | errorResponseBody.err = error; 84 | return res.status(STATUS.INTERNAL_SERVER_ERROR).json(errorResponseBody); 85 | } 86 | } 87 | 88 | const validateResetPasswordRequest = (req, res, next) => { 89 | // validate old password presence 90 | if(!req.body.oldPassword) { 91 | errorResponseBody.err = 'Missing the old password in the request'; 92 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 93 | } 94 | 95 | // validate new password presence 96 | if(!req.body.newPassword) { 97 | errorResponseBody.err = 'Missing the new password in the request'; 98 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 99 | } 100 | 101 | // we can proceed 102 | next(); 103 | } 104 | 105 | const isAdmin = async (req, res, next) => { 106 | console.log(req.user); 107 | const user = await userService.getUserById(req.user); 108 | if(user.userRole != USER_ROLE.admin) { 109 | errorResponseBody.err = "User is not an admin, cannot proceed with the request" 110 | return res.status(STATUS.UNAUTHORISED).json(errorResponseBody); 111 | } 112 | next(); 113 | } 114 | 115 | const isClient = async (req, res, next) => { 116 | const user = await userService.getUserById(req.user); 117 | if(user.userRole != USER_ROLE.client) { 118 | errorResponseBody.err = "User is not a client, cannot proceed with the request"; 119 | return res.status(STATUS.UNAUTHORISED).json(errorResponseBody); 120 | } 121 | next(); 122 | } 123 | 124 | const isAdminOrClient = async (req, res, next) => { 125 | const user = await userService.getUserById(req.user); 126 | if(user.userRole != USER_ROLE.admin && user.userRole != USER_ROLE.client) { 127 | errorResponseBody.err = "User is neither a client not an admin, cannot proceed with the request"; 128 | return res.status(STATUS.UNAUTHORISED).json(errorResponseBody); 129 | } 130 | next(); 131 | } 132 | 133 | 134 | module.exports = { 135 | validateSignupRequest, 136 | validateSigninRequest, 137 | isAuthenticated, 138 | validateResetPasswordRequest, 139 | isAdmin, 140 | isClient, 141 | isAdminOrClient 142 | } -------------------------------------------------------------------------------- /middlewares/booking.middlewares.js: -------------------------------------------------------------------------------- 1 | const { STATUS, USER_ROLE, BOOKING_STATUS } = require('../utils/constants'); 2 | const { errorResponseBody } = require('../utils/responsebody'); 3 | const ObjectId = require('mongoose').Types.ObjectId; 4 | 5 | const theatreService = require('../services/theatre.service'); 6 | const userService = require('../services/user.service'); 7 | 8 | const validateBookingCreateRequest = async (req, res, next) => { 9 | // validate the theatre id presence 10 | if(!req.body.theatreId) { 11 | errorResponseBody.err = "No theatre id provided"; 12 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 13 | } 14 | 15 | // validate correct theatre id format 16 | if(!ObjectId.isValid(req.body.theatreId)) { 17 | errorResponseBody.err = "Invalid theatreid provided" 18 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 19 | } 20 | 21 | // check if theatre exists in database 22 | const theatre = await theatreService.getTheatre(req.body.theatreId); 23 | if(!theatre) { 24 | errorResponseBody.err = "No theatre found for the given id"; 25 | return res.status(STATUS.NOT_FOUND).json(errorResponseBody); 26 | } 27 | 28 | // validate movie presence 29 | if(!req.body.movieId) { 30 | errorResponseBody.err = "No movie id present"; 31 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 32 | } 33 | 34 | // validate correct movie if format 35 | if(!ObjectId.isValid(req.body.movieId)) { 36 | errorResponseBody.err = "Invalid movie id format"; 37 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 38 | } 39 | 40 | // validate if movie is running in the theatre or not ? 41 | console.log(theatre.movies.indexOf(req.body.movieId), req.body.movieId); 42 | if(theatre.movies.indexOf(req.body.movieId) == -1) { 43 | errorResponseBody.err = "Given movie is not available in the requested theatre"; 44 | return res.status(STATUS.NOT_FOUND).json(errorResponseBody); 45 | } 46 | 47 | // validate presence of timings 48 | if(!req.body.timing) { 49 | errorResponseBody.err = "No movie timing passed"; 50 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 51 | } 52 | 53 | // validate no of seats presence 54 | if(!req.body.noOfSeats) { 55 | errorResponseBody.err = "No seat provided"; 56 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 57 | } 58 | 59 | // request is correct 60 | next(); 61 | 62 | } 63 | 64 | const canChangeStatus = async (req, res, next) => { 65 | const user = await userService.getUserById(req.user); 66 | if(user.userRole == USER_ROLE.customer && req.body.status && req.body.status != BOOKING_STATUS.cancelled) { 67 | errorResponseBody.err = "You are not allowed to change the booking status"; 68 | return res.status(STATUS.UNAUTHORISED).json(errorResponseBody); 69 | } 70 | next(); 71 | } 72 | 73 | module.exports = { 74 | validateBookingCreateRequest, 75 | canChangeStatus 76 | } -------------------------------------------------------------------------------- /middlewares/movie.middlewares.js: -------------------------------------------------------------------------------- 1 | const badRequestResponse = { 2 | success: false, 3 | err: "", 4 | data: {}, 5 | message: "Malformed Request | Bad Request" 6 | }; 7 | 8 | const { STATUS } = require('../utils/constants'); 9 | 10 | /** 11 | * 12 | * @param req -> HTTP request object 13 | * @param {*} res -> HTTP response object 14 | * @param {*} next -> next middleware function 15 | * @returns -> whether the request is valid or not 16 | */ 17 | const validateMovieCreateRequest = async (req, res, next) => { 18 | // validate the movie name 19 | if(!req.body.name) { 20 | badRequestResponse.err = "The name of the movie is not present in the request"; 21 | return res.status(STATUS.BAD_REQUEST).json(badRequestResponse); 22 | } 23 | 24 | // validate the movie description 25 | if(!req.body.description) { 26 | badRequestResponse.err = "The description of the movie is not present in the request"; 27 | return res.status(STATUS.BAD_REQUEST).json(badRequestResponse); 28 | } 29 | 30 | // validate the movie casts 31 | if(!req.body.casts || 32 | !(req.body.casts instanceof Array) || 33 | req.body.casts.length <= 0 34 | ) { 35 | badRequestResponse.err = "The casts of the movie is not present in the request"; 36 | return res.status(STATUS.BAD_REQUEST).json(badRequestResponse); 37 | } 38 | 39 | // validate the movie trailer url 40 | if(!req.body.trailerUrl) { 41 | badRequestResponse.err = "The trailerUrl of the movie is not present in the request"; 42 | return res.status(STATUS.BAD_REQUEST).json(badRequestResponse); 43 | } 44 | 45 | // validate the release date of the movie 46 | if(!req.body.releaseDate) { 47 | badRequestResponse.err = "The releaseDate of the movie is not present in the request"; 48 | return res.status(STATUS.BAD_REQUEST).json(badRequestResponse); 49 | } 50 | 51 | // validate director of the movie 52 | if(!req.body.director) { 53 | badRequestResponse.err = "The director of the movie is not present in the request"; 54 | return res.status(STATUS.BAD_REQUEST).json(badRequestResponse); 55 | } 56 | next(); 57 | } 58 | 59 | module.exports = { 60 | validateMovieCreateRequest 61 | } -------------------------------------------------------------------------------- /middlewares/payment.middlewares.js: -------------------------------------------------------------------------------- 1 | const { STATUS } = require("../utils/constants"); 2 | const { errorResponseBody } = require("../utils/responsebody") 3 | const ObjectId = require('mongoose').Types.ObjectId; 4 | 5 | const verifyPaymentCreateRequest = async (req, res, next) => { 6 | // validate booking id presence 7 | if(!req.body.bookingId) { 8 | errorResponseBody.err = 'No booking id received'; 9 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 10 | } 11 | // validate correct bookingid 12 | if(!ObjectId.isValid(req.body.bookingId)) { 13 | errorResponseBody.err = 'Invalid booking id'; 14 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 15 | } 16 | // validate amount presence 17 | if(!req.body.amount) { 18 | errorResponseBody.err = 'No amount sent'; 19 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 20 | } 21 | // everything is fine 22 | next(); 23 | 24 | } 25 | 26 | module.exports = { 27 | verifyPaymentCreateRequest 28 | } -------------------------------------------------------------------------------- /middlewares/show.middlewares.js: -------------------------------------------------------------------------------- 1 | const { STATUS } = require('../utils/constants'); 2 | const { errorResponseBody } = require('../utils/responsebody'); 3 | const ObjectId = require('mongoose').Types.ObjectId; 4 | 5 | const validateCreateShowRequest = async (req, res, next) => { 6 | // validate theatre id 7 | if(!req.body.theatreId) { 8 | errorResponseBody.err = "No theatre provided"; 9 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 10 | } 11 | if(!ObjectId.isValid(req.body.theatreId)) { 12 | errorResponseBody.err = "Invalid theatre id"; 13 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 14 | } 15 | // validate movie presence 16 | if(!req.body.movieId) { 17 | errorResponseBody.err = "No movie provided"; 18 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 19 | } 20 | if(!ObjectId.isValid(req.body.movieId)) { 21 | errorResponseBody.err = "Invalid movie id"; 22 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 23 | } 24 | // validate timing presence 25 | if(!req.body.timing) { 26 | errorResponseBody.err = "No timing provided"; 27 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 28 | } 29 | // validate noofseats presence 30 | if(!req.body.noOfSeats) { 31 | errorResponseBody.err = "No seat info provided"; 32 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 33 | } 34 | // validate price presence 35 | if(!req.body.price) { 36 | errorResponseBody.err = "No price information provided"; 37 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 38 | } 39 | next(); 40 | } 41 | 42 | const validateShowUpdateRequest = async (req, res, next) => { 43 | if(req.body.theatreId || req.body.movieId) { 44 | errorResponseBody.err = "We cannot update theatre or movie for an already added show"; 45 | return res.status(STATUS.BAD_REQUEST).json(errorResponseBody); 46 | } 47 | next(); 48 | } 49 | 50 | module.exports = { 51 | validateCreateShowRequest, 52 | validateShowUpdateRequest 53 | } -------------------------------------------------------------------------------- /middlewares/theatre.middleware.js: -------------------------------------------------------------------------------- 1 | const { errorResponseBody } = require('../utils/responsebody'); 2 | 3 | /** 4 | * 5 | * @param req -> HTTP request object 6 | * @param {*} res -> HTTP response object 7 | * @param {*} next -> next middleware function 8 | * @returns -> whether the request is valid or not 9 | */ 10 | const validateTheatreCreateRequest = async (req, res, next) => { 11 | // validate the presence of name 12 | if(!req.body.name) { 13 | errorResponseBody.err = "The name of the theatre is not present in the request"; 14 | return res.status(400).json(errorResponseBody) 15 | } 16 | // validation for the presence of pincode 17 | if(!req.body.pincode) { 18 | errorResponseBody.err = "The pincode of the theatre is not present in the request"; 19 | return res.status(400).json(errorResponseBody); 20 | } 21 | // validation for the presence of city 22 | if(!req.body.city) { 23 | errorResponseBody.err = "The city of the theatre is not present"; 24 | return res.status(400).json(errorResponseBody); 25 | } 26 | next(); // everything is fine move to the next middleware 27 | } 28 | 29 | const validateUpdateMoviesRequest = async (req, res, next) => { 30 | // validattion of insert parameter 31 | if(req.body.insert == undefined) { 32 | errorResponseBody.err = "The insert parameter is missing in the request"; 33 | return res.status(400).json(errorResponseBody); 34 | } 35 | // validate movieIds presence 36 | if(!req.body.movieIds) { 37 | errorResponseBody.err = "No movies present in the request to be updated in theatre"; 38 | return res.status(400).json(errorResponseBody); 39 | } 40 | // validate if movieIds is an array or not 41 | if(!(req.body.movieIds instanceof Array)) { 42 | errorResponseBody.err = "Expected array of movies but found something else"; 43 | return res.status(400).json(errorResponseBody); 44 | } 45 | // validate if movieIds is empty or not 46 | if(req.body.movieIds.length == 0) { 47 | errorResponseBody.err = "No movies present in the array provided"; 48 | return res.status(400).json(errorResponseBody); 49 | } 50 | // everything is fine 51 | next(); 52 | } 53 | 54 | module.exports = { 55 | validateTheatreCreateRequest, 56 | validateUpdateMoviesRequest 57 | } -------------------------------------------------------------------------------- /middlewares/user.middlewares.js: -------------------------------------------------------------------------------- 1 | const { errorResponseBody } = require("../utils/responsebody") 2 | 3 | const validateUpdateUserRequest = (req, res, next) => { 4 | // validate presence of atleast one of the two i.e. userRole or userStatus 5 | if(!(req.body.userRole || req.body.userStatus)) { 6 | errorResponseBody.err = 'Malformed request, please send atleast one parameter'; 7 | return res.status(400).json(errorResponseBody); 8 | } 9 | next(); 10 | } 11 | 12 | module.exports = { 13 | validateUpdateUserRequest 14 | } -------------------------------------------------------------------------------- /models/booking.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const { BOOKING_STATUS } = require('../utils/constants'); 4 | 5 | const bookingSchema = new mongoose.Schema({ 6 | theatreId: { 7 | type: mongoose.Schema.Types.ObjectId, 8 | required: true, 9 | ref: 'Theatre' 10 | }, 11 | movieId: { 12 | type: mongoose.Schema.Types.ObjectId, 13 | required: true, 14 | ref: 'Movie' 15 | }, 16 | userId: { 17 | type: mongoose.Schema.Types.ObjectId, 18 | required: true, 19 | ref: 'User' 20 | }, 21 | timing: { 22 | type: String, 23 | required: true 24 | }, 25 | noOfSeats: { 26 | type: Number, 27 | required: true, 28 | }, 29 | totalCost: { 30 | type: Number, 31 | required: true 32 | }, 33 | status: { 34 | type: String, 35 | required: true, 36 | enum: { 37 | values: [BOOKING_STATUS.processing, BOOKING_STATUS.cancelled, BOOKING_STATUS.successfull, BOOKING_STATUS.expired], 38 | message: "Invalid booking status" 39 | }, 40 | default: BOOKING_STATUS.processing 41 | }, 42 | seat: { 43 | type: String, 44 | } 45 | }, {timestamps: true}); 46 | 47 | const Booking = mongoose.model('Booking', bookingSchema); 48 | 49 | module.exports = Booking; -------------------------------------------------------------------------------- /models/movie.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | /** 4 | * Define the schema of the movie resource to be stored in the db 5 | */ 6 | const movieSchema = new mongoose.Schema({ 7 | name: { 8 | type: String, 9 | required: true, 10 | minLength: 2 11 | }, 12 | description: { 13 | type: String, 14 | required: true, 15 | minLength: 5 16 | }, 17 | casts: { 18 | type: [String], 19 | required: true 20 | }, 21 | trailerUrl: { 22 | type: String, 23 | required: true 24 | }, 25 | language: { 26 | type: String, 27 | required: true, 28 | default: "English" 29 | }, 30 | releaseDate: { 31 | type: String, 32 | required: true 33 | }, 34 | director: { 35 | type: String, 36 | required: true 37 | }, 38 | releaseStatus: { 39 | type: String, 40 | required: true, 41 | default: "RELEASED", 42 | }, 43 | poster: { 44 | type: String, 45 | required: true, 46 | } 47 | }, {timestamps: true}); 48 | 49 | const Movie = mongoose.model('Movie', movieSchema); // creates a new model 50 | 51 | module.exports = Movie; // returning the model -------------------------------------------------------------------------------- /models/payment.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const { PAYMENT_STATUS } = require('../utils/constants'); 4 | 5 | const paymentSchema = new mongoose.Schema({ 6 | booking: { 7 | type: mongoose.Schema.Types.ObjectId, 8 | required: true, 9 | ref: 'Booking' 10 | }, 11 | amount: { 12 | type: Number, 13 | required: true, 14 | }, 15 | status: { 16 | type: String, 17 | required: true, 18 | enum: { 19 | values: [PAYMENT_STATUS.pending, PAYMENT_STATUS.failed, PAYMENT_STATUS.success], 20 | message: "Invalid payment status" 21 | }, 22 | default: PAYMENT_STATUS.pending 23 | } 24 | }, {timestamps: true}); 25 | 26 | const payment = mongoose.model('Payment', paymentSchema); 27 | 28 | module.exports = payment; -------------------------------------------------------------------------------- /models/show.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const showSchema = new mongoose.Schema({ 4 | theatreId: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | required: true, 7 | ref: 'Theatre' 8 | }, 9 | movieId: { 10 | type: mongoose.Schema.Types.ObjectId, 11 | required: true, 12 | ref: 'Movie' 13 | }, 14 | timing: { 15 | type: String, 16 | required: true 17 | }, 18 | noOfSeats: { 19 | type: Number, 20 | required: true 21 | }, 22 | seatConfiguration: { 23 | type: String, 24 | }, 25 | price: { 26 | type: Number, 27 | required: true 28 | }, 29 | format: { 30 | type: String 31 | } 32 | }, {timestamps: true}); 33 | 34 | const Show = mongoose.model('Show', showSchema); 35 | 36 | module.exports = Show; -------------------------------------------------------------------------------- /models/theatre.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | /** 3 | * Defines the schema of theatre resource to be stored in the db 4 | */ 5 | 6 | const theatreSchema = new mongoose.Schema({ 7 | name: { 8 | type: String, 9 | required: true, 10 | minLength: 5 11 | }, 12 | description: String, 13 | city: { 14 | type: String, 15 | required: true 16 | }, 17 | pincode: { 18 | type: Number, 19 | required: true 20 | }, 21 | address: String, 22 | owner: { 23 | type: mongoose.Schema.Types.ObjectId, 24 | ref: 'User', 25 | required: true 26 | }, 27 | movies: { 28 | type: [mongoose.Schema.Types.ObjectId], 29 | ref: 'Movie' 30 | } 31 | }, {timestamps: true}); 32 | 33 | const Theatre = mongoose.model('Theatre', theatreSchema); 34 | 35 | module.exports = Theatre; 36 | -------------------------------------------------------------------------------- /models/user.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const bcrypt = require('bcrypt'); 3 | const { USER_ROLE, USER_STATUS} = require('../utils/constants'); 4 | 5 | const userSchema = new mongoose.Schema({ 6 | name: { 7 | type: String, 8 | required: true, 9 | unique: true 10 | }, 11 | email: { 12 | type: String, 13 | required: true, 14 | unique: true, 15 | match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email'], 16 | lowercase: true, 17 | trim: true 18 | }, 19 | password: { 20 | type: String, 21 | required: true, 22 | minLength: 6 23 | }, 24 | userRole: { 25 | type: String, 26 | required: true, 27 | enum: { 28 | values: [USER_ROLE.customer, USER_ROLE.admin, USER_ROLE.client], 29 | message: "Invalid user role given" 30 | }, 31 | default: USER_ROLE.customer 32 | }, 33 | userStatus: { 34 | type: String, 35 | required: true, 36 | enum: { 37 | values: [USER_STATUS.approved, USER_STATUS.pending, USER_STATUS.rejected], 38 | message: "Invalid status for user given" 39 | }, 40 | default: USER_STATUS.approved 41 | } 42 | }, {timestamps: true}); 43 | 44 | userSchema.pre('save', async function (next) { 45 | // a trigger to encrypt the plain password before saving the user 46 | const hash = await bcrypt.hash(this.password, 10); 47 | this.password = hash; 48 | next(); 49 | }); 50 | 51 | /** 52 | * This is going to be an instance method for user, to compare a password 53 | * with the stored encrypted password 54 | * @param plainPassword -> input password given by user in sign in request 55 | * @returns boolean denoting whether passwords are same or not ? 56 | */ 57 | userSchema.methods.isValidPassword = async function (plainPassword) { 58 | const currentUser = this; 59 | const compare = await bcrypt.compare(plainPassword, currentUser.password); 60 | return compare; 61 | } 62 | 63 | const User = mongoose.model('User', userSchema); 64 | module.exports = User; 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-movie-booking-backend-app", 3 | "version": "1.0.0", 4 | "description": "This is a backend API application for Movie Booking, similar to BMS", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "npx nodemon index.js" 9 | }, 10 | "author": "Sanket Singh", 11 | "license": "ISC", 12 | "dependencies": { 13 | "axios": "^0.27.2", 14 | "bcrypt": "^5.0.1", 15 | "body-parser": "^1.20.0", 16 | "cors": "^2.8.5", 17 | "dotenv": "^16.0.1", 18 | "express": "^4.18.1", 19 | "jsonwebtoken": "^8.5.1", 20 | "mongoose": "^6.5.3", 21 | "nodemon": "^2.0.19" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /routes/auth.routes.js: -------------------------------------------------------------------------------- 1 | const authController = require('../controllers/auth.controller'); 2 | const authMiddleware = require('../middlewares/auth.middlewares'); 3 | 4 | const routes = (app) => { 5 | app.post( 6 | '/mba/api/v1/auth/signup', 7 | authMiddleware.validateSignupRequest, 8 | authController.signup 9 | ); 10 | 11 | app.post( 12 | '/mba/api/v1/auth/signin', 13 | authMiddleware.validateSigninRequest, 14 | authController.signin 15 | ); 16 | 17 | app.patch( 18 | '/mba/api/v1/auth/reset', 19 | authMiddleware.isAuthenticated, 20 | authMiddleware.validateResetPasswordRequest, 21 | authController.resetPassword 22 | ); 23 | } 24 | 25 | module.exports = routes; -------------------------------------------------------------------------------- /routes/booking.routes.js: -------------------------------------------------------------------------------- 1 | const bookingController = require('../controllers/booking.controller'); 2 | 3 | const authMiddleware = require('../middlewares/auth.middlewares'); 4 | const bookingMiddleware = require('../middlewares/booking.middlewares'); 5 | 6 | const routes = (app) => { 7 | app.post( 8 | '/mba/api/v1/bookings', 9 | authMiddleware.isAuthenticated, 10 | bookingMiddleware.validateBookingCreateRequest, 11 | bookingController.create 12 | ); 13 | 14 | app.patch( 15 | '/mba/api/v1/bookings/:id', 16 | authMiddleware.isAuthenticated, 17 | bookingMiddleware.canChangeStatus, 18 | bookingController.update 19 | ); 20 | 21 | app.get( 22 | '/mba/api/v1/bookings', 23 | authMiddleware.isAuthenticated, 24 | bookingController.getBookings 25 | ); 26 | 27 | app.get( 28 | '/mba/api/v1/bookings/all', 29 | authMiddleware.isAuthenticated, 30 | authMiddleware.isAdmin, 31 | bookingController.getAllBookings 32 | ); 33 | 34 | app.get( 35 | '/mba/api/v1/bookings/:id', 36 | authMiddleware.isAuthenticated, 37 | bookingController.getBookingById 38 | ); 39 | } 40 | 41 | module.exports = routes; -------------------------------------------------------------------------------- /routes/movie.routes.js: -------------------------------------------------------------------------------- 1 | const movieController = require('../controllers/movie.controller'); 2 | const movieMiddlewares = require('../middlewares/movie.middlewares'); 3 | const authMiddlewares = require('../middlewares/auth.middlewares'); 4 | 5 | const routes = (app) => { 6 | // routes function takes express app object as parameter 7 | 8 | // CREATE 9 | app.post( 10 | '/mba/api/v1/movies', 11 | authMiddlewares.isAuthenticated, 12 | authMiddlewares.isAdminOrClient, 13 | movieMiddlewares.validateMovieCreateRequest, 14 | movieController.createMovie 15 | ); 16 | 17 | // DELETE 18 | app.delete( 19 | '/mba/api/v1/movies/:id', 20 | authMiddlewares.isAuthenticated, 21 | authMiddlewares.isAdminOrClient, 22 | movieController.deleteMovie 23 | ); 24 | 25 | // READ 26 | app.get( 27 | '/mba/api/v1/movies/:id', 28 | movieController.getMovie 29 | ); 30 | 31 | // READ 32 | app.put( 33 | '/mba/api/v1/movies/:id', 34 | authMiddlewares.isAuthenticated, 35 | authMiddlewares.isAdminOrClient, 36 | movieController.updateMovie 37 | ); 38 | 39 | // UPDATE 40 | app.patch( 41 | '/mba/api/v1/movies/:id', 42 | authMiddlewares.isAuthenticated, 43 | authMiddlewares.isAdminOrClient, 44 | movieController.updateMovie 45 | ); 46 | 47 | // UPDATE 48 | app.get( 49 | '/mba/api/v1/movies', 50 | movieController.getMovies 51 | ); 52 | } 53 | 54 | module.exports = routes; -------------------------------------------------------------------------------- /routes/payment.routes.js: -------------------------------------------------------------------------------- 1 | const paymentController = require('../controllers/payment.controller'); 2 | const authMiddlewares = require('../middlewares/auth.middlewares'); 3 | const paymentMiddlewares = require('../middlewares/payment.middlewares'); 4 | const routes = (app) => { 5 | app.post( 6 | '/mba/api/v1/payments', 7 | authMiddlewares.isAuthenticated, 8 | paymentMiddlewares.verifyPaymentCreateRequest, 9 | paymentController.create 10 | ); 11 | 12 | app.get( 13 | '/mba/api/v1/payments/:id', 14 | authMiddlewares.isAuthenticated, 15 | paymentController.getPaymentDetailsById 16 | ); 17 | 18 | app.get( 19 | '/mba/api/v1/payments', 20 | authMiddlewares.isAuthenticated, 21 | paymentController.getAllPayments 22 | ); 23 | } 24 | 25 | module.exports = routes; -------------------------------------------------------------------------------- /routes/show.routes.js: -------------------------------------------------------------------------------- 1 | const showController = require('../controllers/show.controller'); 2 | const authMiddlewares = require('../middlewares/auth.middlewares'); 3 | const showMiddlewares = require('../middlewares/show.middlewares'); 4 | 5 | const routes = (app) => { 6 | app.post( 7 | '/mba/api/v1/shows', 8 | authMiddlewares.isAuthenticated, 9 | authMiddlewares.isAdminOrClient, 10 | showMiddlewares.validateCreateShowRequest, 11 | showController.create 12 | ); 13 | 14 | app.get( 15 | '/mba/api/v1/shows', 16 | showController.getShows 17 | ); 18 | 19 | app.delete( 20 | '/mba/api/v1/shows/:id', 21 | authMiddlewares.isAuthenticated, 22 | authMiddlewares.isAdminOrClient, 23 | showController.destroy 24 | ); 25 | 26 | app.patch( 27 | '/mba/api/v1/shows/:id', 28 | authMiddlewares.isAuthenticated, 29 | authMiddlewares.isAdminOrClient, 30 | showMiddlewares.validateShowUpdateRequest, 31 | showController.update 32 | ); 33 | } 34 | 35 | module.exports = routes; -------------------------------------------------------------------------------- /routes/theatre.routes.js: -------------------------------------------------------------------------------- 1 | const theatreController = require('../controllers/theatre.controller'); 2 | const theatreMiddleware = require('../middlewares/theatre.middleware'); 3 | const authMiddleware = require('../middlewares/auth.middlewares'); 4 | 5 | const routes = (app) => { 6 | // routes function takes express app object as parameter 7 | 8 | // CREATE 9 | app.post( 10 | '/mba/api/v1/theatres', 11 | authMiddleware.isAuthenticated, 12 | authMiddleware.isAdminOrClient, 13 | theatreMiddleware.validateTheatreCreateRequest, 14 | theatreController.create 15 | ); 16 | 17 | // DELETE 18 | app.delete( 19 | '/mba/api/v1/theatres/:id', 20 | authMiddleware.isAuthenticated, 21 | authMiddleware.isAdminOrClient, 22 | theatreController.destroy 23 | ); 24 | 25 | // READ 26 | app.get( 27 | '/mba/api/v1/theatres/:id', 28 | theatreController.getTheatre 29 | ); 30 | 31 | // READ 32 | app.get( 33 | '/mba/api/v1/theatres', 34 | theatreController.getTheatres 35 | ); 36 | 37 | // UPDATE 38 | app.patch( 39 | '/mba/api/v1/theatres/:id', 40 | authMiddleware.isAuthenticated, 41 | authMiddleware.isAdminOrClient, 42 | theatreController.update 43 | ); 44 | 45 | // UPDATE 46 | app.put( 47 | '/mba/api/v1/theatres/:id', 48 | authMiddleware.isAuthenticated, 49 | authMiddleware.isAdminOrClient, 50 | theatreController.update 51 | ); 52 | 53 | app.patch( 54 | '/mba/api/v1/theatres/:id/movies', 55 | theatreMiddleware.validateUpdateMoviesRequest, 56 | theatreController.updateMovies 57 | ); 58 | 59 | app.get( 60 | '/mba/api/v1/theatres/:id/movies', 61 | theatreController.getMovies 62 | ) 63 | 64 | app.get( 65 | '/mba/api/v1/theatres/:theatreId/movies/:movieId', 66 | theatreController.checkMovie 67 | ); 68 | } 69 | 70 | module.exports = routes; -------------------------------------------------------------------------------- /routes/user.routes.js: -------------------------------------------------------------------------------- 1 | const userController = require('../controllers/user.controller'); 2 | const userMiddleware = require('../middlewares/user.middlewares'); 3 | const authMiddleware = require('../middlewares/auth.middlewares'); 4 | 5 | const route = (app) => { 6 | app.patch( 7 | '/mba/api/v1/user/:id', 8 | authMiddleware.isAuthenticated, 9 | userMiddleware.validateUpdateUserRequest, 10 | authMiddleware.isAdmin, 11 | userController.update 12 | ) 13 | } 14 | 15 | module.exports = route; -------------------------------------------------------------------------------- /services/booking.services.js: -------------------------------------------------------------------------------- 1 | const Booking = require('../models/booking.model'); 2 | const Show = require('../models/show.model'); 3 | 4 | const { STATUS } = require('../utils/constants'); 5 | 6 | const createBooking = async (data) => { 7 | try { 8 | const show = await Show.findOne({ 9 | movieId: data.movieId, 10 | theatreId: data.theatreId, 11 | _id: data.showId 12 | }); 13 | console.log(data); 14 | console.log(show.price, data.noOfSeats); 15 | data.totalCost = data.noOfSeats*show.price; 16 | const response = await Booking.create(data); 17 | await show.save(); 18 | return response.populate('movieId theatreId'); 19 | } catch (error) { 20 | console.log(error); 21 | if(error.name == 'ValidationError') { 22 | let err = {}; 23 | Object.keys(error.errors).forEach(key => { 24 | err[key] = error.errors[key].message; 25 | }); 26 | throw {err: err, code: STATUS.UNPROCESSABLE_ENTITY}; 27 | } 28 | throw error; 29 | } 30 | } 31 | 32 | const updateBooking = async (data, bookingId) => { 33 | try { 34 | const response = await Booking.findByIdAndUpdate(bookingId, data, { 35 | new: true, runValidators: true 36 | }); 37 | if(!response) { 38 | throw { 39 | err: "No booking found for the given id", 40 | code: STATUS.NOT_FOUND 41 | } 42 | } 43 | return response; 44 | } catch (error) { 45 | if(error.name == 'ValidationError') { 46 | let err = {}; 47 | Object.keys(error.errors).forEach(key => { 48 | err[key] = error.errors[key].message; 49 | }); 50 | throw {err: err, code: STATUS.UNPROCESSABLE_ENTITY}; 51 | } 52 | console.log(error); 53 | throw error; 54 | } 55 | } 56 | 57 | const getBookings = async (data) => { 58 | try { 59 | const response = await Booking.find(data); 60 | return response; 61 | } catch (error) { 62 | throw error; 63 | } 64 | } 65 | 66 | const getAllBookings = async () => { 67 | try { 68 | const response = await Booking.find(); 69 | return response; 70 | } catch (error) { 71 | throw error; 72 | } 73 | } 74 | 75 | const getBookingById = async (id, userId) => { 76 | try { 77 | const response = await Booking.findById(id); 78 | if(!response) { 79 | throw { 80 | err: 'No booking records found for the id', 81 | code: STATUS.NOT_FOUND 82 | } 83 | } 84 | if(response.userId != userId) { 85 | throw { 86 | err: 'Not able to access the booking', 87 | code: STATUS.UNAUTHORISED 88 | } 89 | } 90 | return response; 91 | } catch (error) { 92 | console.log(error); 93 | throw error; 94 | } 95 | } 96 | 97 | module.exports = { 98 | createBooking, 99 | updateBooking, 100 | getBookings, 101 | getAllBookings, 102 | getBookingById 103 | } -------------------------------------------------------------------------------- /services/email.service.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | const User = require('../models/user.model'); 3 | const sendMail = async (subject, id, content) => { 4 | const user = await User.findById(id); 5 | axios.post(process.env.NOTI_SERVICE + '/notiservice/api/v1/notifications', { 6 | subject: subject, 7 | recepientEmails: [user.email] , 8 | content: content 9 | }); 10 | } 11 | 12 | module.exports = sendMail; -------------------------------------------------------------------------------- /services/movie.service.js: -------------------------------------------------------------------------------- 1 | const Movie = require('../models/movie.model'); 2 | const { STATUS } = require('../utils/constants'); 3 | 4 | /** 5 | * 6 | * @param data -> object containing details of the new movie to be created 7 | * @returns -> returns the new movie object created 8 | */ 9 | const createMovie = async (data) => { 10 | try { 11 | const movie = await Movie.create(data ); 12 | return movie; 13 | } catch (error) { 14 | if(error.name == 'ValidationError') { 15 | let err = {}; 16 | Object.keys(error.errors).forEach((key) => { 17 | err[key] = error.errors[key].message; 18 | }); 19 | console.log(err); 20 | throw {err: err, code: STATUS.UNPROCESSABLE_ENTITY}; 21 | } else { 22 | throw error; 23 | } 24 | } 25 | } 26 | 27 | /** 28 | * 29 | * @param id -> id which will be used to indentify the movie to be deleted 30 | * @returns -> object containing details of the movie deleted 31 | */ 32 | const deleteMovie = async (id) => { 33 | try { 34 | const response = await Movie.findByIdAndDelete(id); 35 | if(!response) { 36 | throw { 37 | err: "No movie record found for the id provided", 38 | code: STATUS.NOT_FOUND 39 | } 40 | } 41 | return response 42 | } catch (error) { 43 | console.log(error); 44 | throw error; 45 | } 46 | } 47 | 48 | /** 49 | * 50 | * @param id -> id which will be used to identify the movie to be fetched 51 | * @returns -> object containing movie fetched 52 | */ 53 | const getMoviById = async (id) => { 54 | const movie = await Movie.findById(id); 55 | if(!movie) { 56 | throw { 57 | err: "No movie found for the corresponding id provided", 58 | code: STATUS.NOT_FOUND 59 | } 60 | }; 61 | return movie; 62 | } 63 | 64 | /** 65 | * 66 | * @param id -> id which will be used to identify the movie to be updated 67 | * @param data -> object that contains actual data which is to be updated in the db 68 | * @returns -> returns the new updated movie details 69 | */ 70 | const updateMovie = async (id, data) => { 71 | try { 72 | const movie = await Movie.findByIdAndUpdate(id, data, {new: true, runValidators: true}); 73 | return movie; 74 | } catch (error) { 75 | if(error.name == 'ValidationError') { 76 | let err = {}; 77 | Object.keys(error.errors).forEach((key) => { 78 | err[key] = error.errors[key].message; 79 | }); 80 | console.log(err); 81 | throw {err: err, code: STATUS.UNPROCESSABLE_ENTITY}; 82 | } else { 83 | throw error; 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * 90 | * @param filter -> filter will help us in filtering out data based on the conditionals it contains 91 | * @returns -> returns an object containing all the movies fetched based on the filter 92 | */ 93 | const fetchMovies = async (filter) => { 94 | let query = {}; 95 | if(filter.name) { 96 | query.name = filter.name; 97 | } 98 | let movies = await Movie.find(query); 99 | if(!movies) { 100 | throw { 101 | err: 'Not able to find the queries movies', 102 | code: STATUS.NOT_FOUND 103 | } 104 | } 105 | return movies; 106 | } 107 | 108 | module.exports = { 109 | createMovie, 110 | deleteMovie, 111 | getMoviById, 112 | updateMovie, 113 | fetchMovies 114 | } -------------------------------------------------------------------------------- /services/payment.service.js: -------------------------------------------------------------------------------- 1 | const Payment = require('../models/payment.model'); 2 | const Booking = require('../models/booking.model'); 3 | const Show = require('../models/show.model'); 4 | const User = require('../models/user.model'); 5 | 6 | const { STATUS, BOOKING_STATUS, PAYMENT_STATUS, USER_ROLE } = require('../utils/constants'); 7 | 8 | const createPayment = async (data) => { 9 | try { 10 | const booking = await Booking.findById(data.bookingId); 11 | const show = await Show.findOne({ 12 | movieId: booking.movieId, 13 | theatreId: booking.theatreId, 14 | showId: data.showId 15 | }); 16 | if(booking.status == BOOKING_STATUS.successfull) { 17 | throw { 18 | err: 'Booking already done, cannot make a new payment against it', 19 | code: STATUS.FORBIDDEN 20 | } 21 | } 22 | if(!booking) { 23 | throw { 24 | err: 'No booking found', 25 | code: STATUS.NOT_FOUND 26 | } 27 | } 28 | let bookingTime = booking.createdAt; 29 | let currentTime = Date.now(); 30 | 31 | // calculate how many minutes are remaining 32 | let minutes = Math.floor(((currentTime - bookingTime) / 1000) / 60); 33 | if(minutes > 5) { 34 | booking.status = BOOKING_STATUS.expired; 35 | await booking.save(); 36 | return booking; 37 | } 38 | 39 | const payment = await Payment.create({ 40 | booking: data.bookingId, 41 | amount: data.amount 42 | }); 43 | if(payment.amount != booking.totalCost) { 44 | payment.status = PAYMENT_STATUS.failed; 45 | } 46 | if(!payment || payment.status == PAYMENT_STATUS.failed) { 47 | booking.status = BOOKING_STATUS.cancelled; 48 | await booking.save(); 49 | await payment.save(); 50 | return booking; 51 | } 52 | payment.status = PAYMENT_STATUS.success; 53 | booking.status = BOOKING_STATUS.successfull; 54 | console.log(show, booking) 55 | show.noOfSeats -= booking.noOfSeats; 56 | 57 | if(show.seatConfiguration) { 58 | const showSeatConfig = JSON.parse(show.seatConfiguration.replaceAll("'", '"')); 59 | const bookedSeats = JSON.parse(booking.seat.replaceAll("'", '"')); 60 | const bookedSeatsMap = {}; 61 | bookedSeats.forEach((seats) => { 62 | if(!bookedSeatsMap[seats.rowNumber]) { 63 | bookedSeatsMap[seats.rowNumber] = new Set(); 64 | } 65 | bookedSeatsMap[seats.rowNumber].add(seats.seatNumber); 66 | }); 67 | showSeatConfig.rows.forEach((row) => { 68 | if(bookedSeatsMap[row.number]) { 69 | row.seats = row.seats.map((seat) => { 70 | if(bookedSeatsMap[row.number].has(seat.number)) { 71 | seat.status = 2; 72 | } 73 | return seat; 74 | }) 75 | } 76 | }); 77 | show.seatConfiguration = JSON.stringify(showSeatConfig).replaceAll('"', "'"); 78 | } 79 | 80 | await show.save(); 81 | await booking.save(); 82 | await payment.save(); 83 | return booking; 84 | } catch (error) { 85 | console.log(error.message); 86 | throw error; 87 | } 88 | } 89 | 90 | const getPaymentById = async (id) => { 91 | try { 92 | const response = await Payment.findById(id).populate('booking'); 93 | if(!response) { 94 | throw { 95 | err: 'No payment record found', 96 | code: STATUS.NOT_FOUND 97 | } 98 | } 99 | return response; 100 | } catch (error) { 101 | console.log(error); 102 | throw error; 103 | } 104 | } 105 | 106 | const getAllPayments = async (userId) => { 107 | try { 108 | const user = await User.findById(userId); 109 | let filter = {}; 110 | if(user.userRole != USER_ROLE.admin) { 111 | filter.userId = user.id; 112 | } 113 | const bookings = await Booking.find(filter, 'id'); 114 | 115 | const payments = await Payment.find({booking: {$in: bookings}}); 116 | return payments; 117 | } catch (error) { 118 | throw error; 119 | } 120 | } 121 | 122 | 123 | module.exports = { 124 | createPayment, 125 | getPaymentById, 126 | getAllPayments 127 | } -------------------------------------------------------------------------------- /services/show.service.js: -------------------------------------------------------------------------------- 1 | const Show = require('../models/show.model'); 2 | const Theatre = require('../models/theatre.model'); 3 | const { STATUS } = require('../utils/constants'); 4 | 5 | /** 6 | * 7 | * @param data -> object containing details of the show to be created 8 | * @returns -> object with the new show details 9 | */ 10 | const createShow = async (data) => { 11 | try { 12 | const theatre = await Theatre.findById(data.theatreId); 13 | if(!theatre) { 14 | throw { 15 | err: 'No theatre found', 16 | code: STATUS.NOT_FOUND 17 | } 18 | } 19 | if(theatre.movies.indexOf(data.movieId) == -1) { 20 | throw { 21 | err: 'Movie is currently not available in the requested theatre', 22 | code: STATUS.NOT_FOUND 23 | } 24 | } 25 | const response = await Show.create(data); 26 | return response; 27 | } catch (error) { 28 | if(error.name == 'ValidationError') { 29 | let err = {}; 30 | Object.keys(error.errors).forEach(key => { 31 | err[key] = error.errors[key].message; 32 | }); 33 | throw { 34 | err, 35 | code: STATUS.UNPROCESSABLE_ENTITY 36 | } 37 | } 38 | throw error; 39 | } 40 | } 41 | 42 | const getShows = async (data) => { 43 | try { 44 | let filter = {}; 45 | if(data.theatreId) { 46 | filter.theatreId = data.theatreId; 47 | } 48 | if(data.movieId) { 49 | filter.movieId = data.movieId; 50 | } 51 | const response = await Show.find(filter).populate('theatreId'); 52 | if(!response) { 53 | throw { 54 | err: 'No shows found', 55 | code: STATUS.NOT_FOUND 56 | } 57 | } 58 | return response; 59 | } catch (error) { 60 | throw error; 61 | } 62 | } 63 | 64 | const deleteShow = async (id) => { 65 | try { 66 | const response = await Show.findByIdAndDelete(id); 67 | if(!response) { 68 | throw { 69 | err: 'No show found', 70 | code: STATUS.NOT_FOUND 71 | } 72 | } 73 | return response; 74 | } catch (error) { 75 | throw error; 76 | } 77 | } 78 | 79 | const updateShow = async (id, data) => { 80 | try { 81 | const response = await Show.findByIdAndUpdate(id, data, { 82 | new: true, 83 | runValidators: true 84 | }); 85 | if(!response) { 86 | throw { 87 | err: 'No show found for the given id', 88 | code: STATUS.NOT_FOUND 89 | } 90 | } 91 | return response; 92 | } catch (error) { 93 | if(error.name == 'ValidationError') { 94 | let err = {}; 95 | Object.keys(error.errors).forEach(key => { 96 | err[key] = error.errors[key].message; 97 | }); 98 | throw { 99 | err, 100 | code: STATUS.UNPROCESSABLE_ENTITY 101 | } 102 | } 103 | throw error; 104 | } 105 | } 106 | 107 | module.exports = { 108 | createShow, 109 | getShows, 110 | deleteShow, 111 | updateShow 112 | } -------------------------------------------------------------------------------- /services/theatre.service.js: -------------------------------------------------------------------------------- 1 | const Theatre = require('../models/theatre.model'); 2 | const Movie = require('../models/movie.model'); 3 | const { STATUS } = require('../utils/constants'); 4 | 5 | /** 6 | * 7 | * @param data -> object containing details of the theatre to be created 8 | * @returns -> object with the new theatre details 9 | */ 10 | const createTheatre = async (data) => { 11 | try { 12 | const response = await Theatre.create(data); 13 | return response; 14 | } catch (error) { 15 | if(error.name == 'ValidationError') { 16 | let err = {}; 17 | Object.keys(error.errors).forEach((key) => { 18 | err[key] = error.errors[key].message; 19 | }); 20 | throw {err: err, code: STATUS.UNPROCESSABLE_ENTITY}; 21 | } 22 | console.log(error); 23 | throw err; 24 | } 25 | } 26 | 27 | /** 28 | * 29 | * @param id -> the unique id using which we can identify the theatre to be deleted 30 | * @returns -> returns the deleted theatre object 31 | */ 32 | const deleteTheatre = async (id) => { 33 | try { 34 | const response = await Theatre.findByIdAndDelete(id); 35 | if(!response) { 36 | throw { 37 | err: "No record of a theatre found for the given id", 38 | code: STATUS.NOT_FOUND 39 | } 40 | } 41 | return response; 42 | } catch (error) { 43 | console.log(error); 44 | throw error; 45 | } 46 | } 47 | 48 | /** 49 | * 50 | * @param id -> it is the unique _id based on which we will fetch a theatre 51 | */ 52 | const getTheatre = async (id) => { 53 | try { 54 | const response = await Theatre.findById(id); 55 | if(!response) { 56 | // no record found for the given id 57 | throw { 58 | err: "No theatre found for the given id", 59 | code: STATUS.NOT_FOUND 60 | } 61 | } 62 | return response; 63 | } catch (error) { 64 | console.log(error); 65 | throw error; 66 | } 67 | } 68 | 69 | /** 70 | * 71 | * @param data -> the data to be used to filter out theatres based on city / pincode 72 | * @returns -> returns an object with the filtered content of theatres 73 | */ 74 | const getAllTheatres = async (data) => { 75 | try { 76 | let query = {}; 77 | let pagination = {}; 78 | if(data && data.city) { 79 | // this checks whether city is present in query params or not 80 | query.city = data.city; 81 | } 82 | if(data && data.pincode) { 83 | // this checks whether pincode is present in query params or not 84 | query.pincode = data.pincode; 85 | } 86 | if(data && data.name) { 87 | // this checks whether name is present in query params or not 88 | query.name = data.name; 89 | } 90 | 91 | if(data && data.movieId) { 92 | query.movies = {$all: data.movieId}; 93 | } 94 | 95 | if(data && data.limit) { 96 | pagination.limit = data.limit; 97 | } 98 | 99 | if(data && data.skip) { 100 | // for first page we send skip as 0 101 | let perPage = (data.limit) ? data.limit : 3; 102 | pagination.skip = data.skip*perPage; 103 | } 104 | const response = await Theatre.find(query, {}, pagination); // {pincode: 110031, movies: {$all: movie}} 105 | return response; 106 | } catch (error) { 107 | console.log(error); 108 | throw error; 109 | } 110 | } 111 | 112 | /** 113 | * 114 | * @param id -> the unique id to identify the theatre to be updated 115 | * @param data -> data object to be used to update the theatre 116 | * @returns -> it returns the new updated theatre object 117 | */ 118 | const updateTheatre = async (id, data) => { 119 | try { 120 | const response = await Theatre.findByIdAndUpdate(id, data, { 121 | new: true, runValidators: true 122 | }); 123 | if(!response) { 124 | // no record found for the given id 125 | throw { 126 | err: "No theatre found for the given id", 127 | code: STATUS.NOT_FOUND 128 | } 129 | } 130 | return response; 131 | } catch (error) { 132 | if(error.name == 'ValidationError') { 133 | let err = {}; 134 | Object.keys(error.errors).forEach((key) => { 135 | err[key] = error.errors[key].message; 136 | }); 137 | throw {err: err, code: STATUS.UNPROCESSABLE_ENTITY} 138 | } 139 | throw error; 140 | } 141 | } 142 | 143 | /** 144 | * 145 | * @param theatreId -> unique id of the theatre for which we want to update movies 146 | * @param movieIds -> array of movie ids that are expected to be updated in theatre 147 | * @param insert -> boolean that tells whether we want insert movies or remove them 148 | * @returns -> updated theatre object 149 | */ 150 | const updateMoviesInTheatres = async (theatreId, movieIds, insert) => { 151 | try { 152 | let theatre; 153 | if (insert) { 154 | // we need to add movies 155 | theatre = await Theatre.findByIdAndUpdate( 156 | {_id: theatreId}, 157 | {$addToSet: {movies: {$each: movieIds}}}, 158 | {new: true} 159 | ); 160 | } else { 161 | // we need to remove movies 162 | theatre = await Theatre.findByIdAndUpdate( 163 | {_id: theatreId}, 164 | {$pull: {movies: {$in: movieIds}}}, 165 | {new: true} 166 | ); 167 | } 168 | 169 | return theatre.populate('movies'); 170 | } catch (error) { 171 | if(error.name == 'TypeError') { 172 | throw { 173 | code: STATUS.NOT_FOUND, 174 | err: 'No theatre found for the given id' 175 | } 176 | } 177 | console.log("Error is", error); 178 | throw error; 179 | } 180 | } 181 | 182 | const getMoviesInATheatre = async (id) => { 183 | try { 184 | const theatre = await Theatre.findById(id, {name: 1, movies: 1, address: 1}).populate('movies'); 185 | if(!theatre) { 186 | throw { 187 | err: 'No theatre with the given id found', 188 | code: STATUS.NOT_FOUND 189 | } 190 | } 191 | return theatre; 192 | } catch (error) { 193 | console.log(error); 194 | throw error; 195 | } 196 | } 197 | 198 | const checkMovieInATheatre = async (theatreId, movieId) => { 199 | try { 200 | let response = await Theatre.findById(theatreId); 201 | if(!response) { 202 | throw { 203 | err: "No such theatre found for the given id", 204 | code: STATUS.NOT_FOUND 205 | } 206 | } 207 | return response.movies.indexOf(movieId) != -1; 208 | } catch (error) { 209 | console.log(error); 210 | throw error; 211 | } 212 | } 213 | 214 | 215 | module.exports = { 216 | createTheatre, 217 | deleteTheatre, 218 | getTheatre, 219 | getAllTheatres, 220 | updateTheatre, 221 | updateMoviesInTheatres, 222 | getMoviesInATheatre, 223 | checkMovieInATheatre 224 | } -------------------------------------------------------------------------------- /services/user.service.js: -------------------------------------------------------------------------------- 1 | const User = require('../models/user.model'); 2 | const { USER_ROLE, USER_STATUS, STATUS} = require('../utils/constants'); 3 | 4 | const createUser = async (data) => { 5 | try { 6 | if(!data.userRole || data.userRole == USER_ROLE.customer) { 7 | if(data.userStatus && data.userStatus != USER_STATUS.approved) { 8 | throw { 9 | err: "We cannot set any other status for customer", 10 | code: 400 11 | }; 12 | } 13 | } 14 | if(data.userRole && data.userRole != USER_ROLE.customer) { 15 | data.userStatus = USER_STATUS.pending; 16 | } 17 | 18 | const response = await User.create(data); 19 | console.log(response); 20 | return response; 21 | } catch (error) { 22 | console.log(error); 23 | if(error.name == 'ValidationError') { 24 | let err = {}; 25 | Object.keys(error.errors).forEach((key) => { 26 | err[key] = error.errors[key].message; 27 | }); 28 | throw {err: err, code: 422}; 29 | } 30 | throw error; 31 | } 32 | } 33 | 34 | const getUserByEmail = async (email) => { 35 | try { 36 | const response = await User.findOne({ 37 | email: email 38 | }); 39 | if(!response) { 40 | throw {err: "No user found for the given email", code: 404}; 41 | } 42 | return response; 43 | } catch (error) { 44 | console.log(error); 45 | throw error; 46 | } 47 | } 48 | 49 | const getUserById = async (id) => { 50 | try { 51 | const user = await User.findById(id); 52 | if(!user) { 53 | throw {err: "No user found for the given id", code: 404}; 54 | } 55 | return user; 56 | } catch (error) { 57 | console.log(error); 58 | throw error; 59 | } 60 | } 61 | 62 | const updateUserRoleOrStatus = async (data, userId) => { 63 | try { 64 | let updateQuery = {}; 65 | if(data.userRole) updateQuery.userRole = data.userRole; 66 | if(data.userStatus) updateQuery.userStatus = data.userStatus; 67 | 68 | let response = await User.findByIdAndUpdate(userId, updateQuery, {new: true, runValidators: true}); 69 | 70 | if(!response) throw {err: 'No user found for the given id', code: STATUS.NOT_FOUND}; 71 | 72 | return response; 73 | } catch (error) { 74 | console.log(error, error.name); 75 | if(error.name == 'ValidationError') { 76 | let err = {}; 77 | Object.keys(error.errors).forEach(key => { 78 | err[key] = error.errors[key].message; 79 | }); 80 | throw {err: err, code: STATUS.BAD_REQUEST}; 81 | } 82 | throw error; 83 | } 84 | } 85 | 86 | module.exports = { 87 | createUser, 88 | getUserByEmail, 89 | getUserById, 90 | updateUserRoleOrStatus 91 | } -------------------------------------------------------------------------------- /utils/constants.js: -------------------------------------------------------------------------------- 1 | const USER_STATUS = { 2 | approved: "APPROVED", 3 | pending: "PENDING", 4 | rejected: "REJECTED" 5 | }; 6 | 7 | const USER_ROLE = { 8 | customer: "CUSTOMER", 9 | admin: "ADMIN", 10 | client: "CLIENT" 11 | } 12 | 13 | const STATUS_CODES = { 14 | OK: 200, 15 | INTERNAL_SERVER_ERROR: 500, 16 | CREATED: 201, 17 | UNAUTHORISED: 401, 18 | NOT_FOUND: 404, 19 | BAD_REQUEST: 400, 20 | FORBIDDEN: 403, 21 | UNPROCESSABLE_ENTITY: 422, 22 | GONE: 410, 23 | PAYMENT_REQUIRED: 402 24 | } 25 | 26 | const BOOKING_STATUS = { 27 | cancelled: "CANCELLED", 28 | successfull: "SUCCESSFULL", 29 | processing: "IN_PROCESS", 30 | expired: "EXPIRED" 31 | } 32 | 33 | const PAYMENT_STATUS = { 34 | failed: "FAILED", 35 | success: "SUCCESS", 36 | pending: "PENDING" 37 | } 38 | 39 | module.exports = { 40 | USER_ROLE, 41 | BOOKING_STATUS, 42 | USER_STATUS, 43 | PAYMENT_STATUS, 44 | STATUS: STATUS_CODES 45 | } -------------------------------------------------------------------------------- /utils/responsebody.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This object will be used as a template for building error responses 3 | */ 4 | const errorResponseBody = { 5 | err: {}, 6 | data: {}, 7 | message: 'Something went wrong, cannot process the request', 8 | success: false 9 | } 10 | 11 | /** 12 | * This object will be used as a template for building success responses 13 | */ 14 | const successResponseBody = { 15 | err: {}, 16 | data: {}, 17 | message: 'Successfully processed the request', 18 | success: true 19 | } 20 | 21 | module.exports = { 22 | successResponseBody, 23 | errorResponseBody 24 | } --------------------------------------------------------------------------------