moved api from monorepo
This commit is contained in:
		
							
								
								
									
										240
									
								
								middleware/advancedResults.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								middleware/advancedResults.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,240 @@
 | 
			
		||||
const advancedResults = (model, populate) => async (req, res, next) => {
 | 
			
		||||
    if (req.originalUrl.indexOf('admin') === -1)
 | 
			
		||||
        req.query = {
 | 
			
		||||
            ...req.query,
 | 
			
		||||
            $and: [{accessedBy: {$elemMatch: {user: req.user.id}}}],
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if (req.query.search === undefined) req.query.search = ''
 | 
			
		||||
    if (req.query.search)
 | 
			
		||||
        req.query = {...req.query, $text: {$search: req.query.search}}
 | 
			
		||||
 | 
			
		||||
    const filterAndInGame = ['genre', 'os', 'developer', 'publisher', 'series', 'intel', 'wine', 'controller', 'store', 'soundtrack', 'playStatus', 'rating', 'ratinggte', 'ratinglte', 'steamRating', 'steamRatinggte', 'steamRatinglte']
 | 
			
		||||
 | 
			
		||||
    filterAndInGame.map((filter) => {
 | 
			
		||||
        if (req.query[filter] === undefined) req.query[filter] = ''
 | 
			
		||||
        if (req.query[filter]) {
 | 
			
		||||
            let newFilter
 | 
			
		||||
            if (filter === 'os') {
 | 
			
		||||
                newFilter = req.query[filter].split(',').reduce((prev, curr, index, arr) => {
 | 
			
		||||
                    const key = `${[filter]}.${arr[index]}`.toLowerCase()
 | 
			
		||||
                    prev.push({[key]: true})
 | 
			
		||||
                    return prev
 | 
			
		||||
                }, [])
 | 
			
		||||
            } else if (filter === 'store' || filter === 'soundtrack' || filter === 'playStatus') {
 | 
			
		||||
                newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                    prev.push({accessedBy: {$elemMatch: {[filter]: curr}}})
 | 
			
		||||
                    return prev
 | 
			
		||||
                }, [])
 | 
			
		||||
            } else if (filter === 'rating') {
 | 
			
		||||
                if (Object.hasOwn(req.query[filter], 'gte')) {
 | 
			
		||||
                    newFilter = req.query[filter]['gte'].split(',').reduce((prev, curr) => {
 | 
			
		||||
                        prev.push({accessedBy: {$elemMatch: {rating: {$gte: Number(curr)}}}})
 | 
			
		||||
                        return prev
 | 
			
		||||
                    }, [])
 | 
			
		||||
                    req.query.rating = `gte${req.query[filter]['gte']}`
 | 
			
		||||
                } else if (Object.hasOwn(req.query[filter], 'lte')) {
 | 
			
		||||
                    newFilter = req.query[filter]['lte'].split(',').reduce((prev, curr) => {
 | 
			
		||||
                        prev.push({accessedBy: {$elemMatch: {rating: {$lte: Number(curr)}}}})
 | 
			
		||||
                        return prev
 | 
			
		||||
                    }, [])
 | 
			
		||||
                    req.query.rating = `lte${req.query[filter]['lte']}`
 | 
			
		||||
                } else {
 | 
			
		||||
                    newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                        prev.push({accessedBy: {$elemMatch: {rating: Number(curr)}}})
 | 
			
		||||
                        return prev
 | 
			
		||||
                    }, [])
 | 
			
		||||
                }
 | 
			
		||||
            } else if (filter === 'ratinggte') {
 | 
			
		||||
                newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                    prev.push({accessedBy: {$elemMatch: {rating: {$gte: Number(curr)}}}})
 | 
			
		||||
                    return prev
 | 
			
		||||
                }, [])
 | 
			
		||||
                req.query.rating = req.query.ratinggte
 | 
			
		||||
            } else if (filter === 'ratinglte') {
 | 
			
		||||
                newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                    prev.push({accessedBy: {$elemMatch: {rating: {$lte: Number(curr)}}}})
 | 
			
		||||
                    return prev
 | 
			
		||||
                }, [])
 | 
			
		||||
                req.query.rating = req.query.ratinglte
 | 
			
		||||
            } else if (filter === 'steamRating') {
 | 
			
		||||
                if (Object.hasOwn(req.query[filter], 'gte')) {
 | 
			
		||||
                    newFilter = req.query[filter]['gte'].split(',').reduce((prev, curr) => {
 | 
			
		||||
                        prev.push({steamRating: {$gte: Number(curr)}})
 | 
			
		||||
                        return prev
 | 
			
		||||
                    }, [])
 | 
			
		||||
                    req.query.steamRating = `gte${req.query[filter]['gte']}`
 | 
			
		||||
                } else if (Object.hasOwn(req.query[filter], 'lte')) {
 | 
			
		||||
                    newFilter = req.query[filter]['lte'].split(',').reduce((prev, curr) => {
 | 
			
		||||
                        prev.push({steamRating: {$lte: Number(curr)}})
 | 
			
		||||
                        return prev
 | 
			
		||||
                    }, [])
 | 
			
		||||
                    req.query.steamRating = `lte${req.query[filter]['lte']}`
 | 
			
		||||
                } else {
 | 
			
		||||
                    newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                        prev.push({steamRating: Number(curr)})
 | 
			
		||||
                        return prev
 | 
			
		||||
                    }, [])
 | 
			
		||||
                }
 | 
			
		||||
            } else if (filter === 'steamRatinggte') {
 | 
			
		||||
                newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                    prev.push({steamRating: {$gte: Number(curr)}})
 | 
			
		||||
                    return prev
 | 
			
		||||
                }, [])
 | 
			
		||||
                req.query.steamRating = req.query.steamRatinggte
 | 
			
		||||
            } else if (filter === 'steamRatinglte') {
 | 
			
		||||
                newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                    prev.push({steamRating: {$lte: Number(curr)}})
 | 
			
		||||
                    return prev
 | 
			
		||||
                }, [])
 | 
			
		||||
                req.query.steamRating = req.query.steamRatinglte
 | 
			
		||||
            } else {
 | 
			
		||||
                newFilter = req.query[filter].split(',').reduce((prev, curr) => {
 | 
			
		||||
                    prev.push({[filter]: curr})
 | 
			
		||||
                    return prev
 | 
			
		||||
                }, [])
 | 
			
		||||
            }
 | 
			
		||||
            newFilter.forEach(element => req.query.$and.push(element))
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    let query
 | 
			
		||||
 | 
			
		||||
// Copy req.query
 | 
			
		||||
    const reqQuery = {...req.query}
 | 
			
		||||
 | 
			
		||||
// Fields to exclude
 | 
			
		||||
    const removeFields = [
 | 
			
		||||
        'select',
 | 
			
		||||
        'series',
 | 
			
		||||
        'developer',
 | 
			
		||||
        'publisher',
 | 
			
		||||
        'sort',
 | 
			
		||||
        'limit',
 | 
			
		||||
        'page',
 | 
			
		||||
        'search',
 | 
			
		||||
        'os',
 | 
			
		||||
        'playStatus',
 | 
			
		||||
        'store',
 | 
			
		||||
        'soundtrack',
 | 
			
		||||
        'genre',
 | 
			
		||||
        'controller',
 | 
			
		||||
        'rating',
 | 
			
		||||
        'ratinggte',
 | 
			
		||||
        'ratinglte',
 | 
			
		||||
        'steamRating',
 | 
			
		||||
        'steamRatinggte',
 | 
			
		||||
        'steamRatinglte',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
// Loop over removeFields and delete them from reqQuery
 | 
			
		||||
    removeFields.forEach((param) => delete reqQuery[param])
 | 
			
		||||
 | 
			
		||||
    filterAndInGame.forEach((param) => {
 | 
			
		||||
        if (req.query[param] === '') delete reqQuery[param]
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
// Create query String
 | 
			
		||||
    let queryStr = JSON.stringify(reqQuery)
 | 
			
		||||
 | 
			
		||||
// Create operators like gt, gte, etc...
 | 
			
		||||
    queryStr = queryStr.replace(
 | 
			
		||||
        /\b(gt|gte|lt|lte|in|all)\b/g,
 | 
			
		||||
        (match) => `$${match}`,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    queryStr = queryStr.replace(/\$\$/g, '$')
 | 
			
		||||
 | 
			
		||||
    const total = await model.countDocuments(reqQuery)
 | 
			
		||||
 | 
			
		||||
// Finding resource
 | 
			
		||||
    query = model.find(JSON.parse(queryStr))
 | 
			
		||||
 | 
			
		||||
// Select Fields
 | 
			
		||||
    if (req.query.select) {
 | 
			
		||||
        const fields = req.query.select.split(',').join(' ')
 | 
			
		||||
        query = query.select(fields)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// Sort
 | 
			
		||||
    if (req.query.sort) {
 | 
			
		||||
        const sortBy = req.query.sort.split(',').join(' ')
 | 
			
		||||
        query = query.sort(sortBy)
 | 
			
		||||
    } else {
 | 
			
		||||
        query = query.sort('series title')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// Pagination
 | 
			
		||||
    let page = parseInt(req.query.page, 10) || 1
 | 
			
		||||
    const limit = parseInt(req.query.limit, 10) || 10
 | 
			
		||||
 | 
			
		||||
// This block figures out how many total pages there will be and if
 | 
			
		||||
// the client is calling a page larger than the total pages
 | 
			
		||||
    let pages
 | 
			
		||||
    if (limit === 1) pages = total
 | 
			
		||||
    else if (total === 0) pages = 1
 | 
			
		||||
    else if (total % limit === 0) pages = (total - (total % limit)) / limit
 | 
			
		||||
    else pages = (total - (total % limit)) / limit + 1
 | 
			
		||||
    if (page > pages) page = pages
 | 
			
		||||
 | 
			
		||||
    const startIndex = (page - 1) * limit
 | 
			
		||||
    const endIndex = page * limit
 | 
			
		||||
 | 
			
		||||
    query = query.skip(startIndex).limit(limit)
 | 
			
		||||
 | 
			
		||||
    if (populate) {
 | 
			
		||||
        query = query.populate(populate)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
// Executing query
 | 
			
		||||
    const results = await query
 | 
			
		||||
 | 
			
		||||
// Pagination result
 | 
			
		||||
    const pagination = {}
 | 
			
		||||
 | 
			
		||||
    if (endIndex < total) {
 | 
			
		||||
        pagination.next = {
 | 
			
		||||
            page: page + 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (startIndex > 0) {
 | 
			
		||||
        pagination.prev = {
 | 
			
		||||
            page: page - 1,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    res.advancedResults = {
 | 
			
		||||
        success: true,
 | 
			
		||||
        searchParams: req.query.search,
 | 
			
		||||
        count: {
 | 
			
		||||
            gamesPerPage: results.length,
 | 
			
		||||
            totalGames: total,
 | 
			
		||||
            currentPage: page,
 | 
			
		||||
            pages,
 | 
			
		||||
            limit,
 | 
			
		||||
        },
 | 
			
		||||
        filters: {
 | 
			
		||||
            series: req.query.series,
 | 
			
		||||
            playStatus: req.query.playStatus,
 | 
			
		||||
            genre: req.query.genre,
 | 
			
		||||
            os: req.query.os,
 | 
			
		||||
            store: req.query.store,
 | 
			
		||||
            developer: req.query.developer,
 | 
			
		||||
            controller: req.query.controller,
 | 
			
		||||
            publisher: req.query.publisher,
 | 
			
		||||
            soundtrack: req.query.soundtrack,
 | 
			
		||||
            intel: req.query.intel,
 | 
			
		||||
            wine: req.query.wine,
 | 
			
		||||
            rating: req.query.rating,
 | 
			
		||||
            steamRating: req.query.steamRating,
 | 
			
		||||
        },
 | 
			
		||||
        pagination,
 | 
			
		||||
        data: results,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    next()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default advancedResults
 | 
			
		||||
							
								
								
									
										4
									
								
								middleware/async.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								middleware/async.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
const asyncHandler = fn => (req, res, next) =>
 | 
			
		||||
  Promise.resolve(fn(req, res, next)).catch(next)
 | 
			
		||||
 | 
			
		||||
export default asyncHandler
 | 
			
		||||
							
								
								
									
										48
									
								
								middleware/auth.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								middleware/auth.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
import jwt from 'jsonwebtoken'
 | 
			
		||||
import asyncHandler from './async.js'
 | 
			
		||||
import ErrorResponse from '../utils/errorResponse.js'
 | 
			
		||||
import User from '../models/User.js'
 | 
			
		||||
 | 
			
		||||
// Protect route
 | 
			
		||||
export const protect = asyncHandler(async (req, res, next) => {
 | 
			
		||||
  let token
 | 
			
		||||
 | 
			
		||||
  // Set token from header
 | 
			
		||||
  if (
 | 
			
		||||
      req.headers.authorization &&
 | 
			
		||||
      req.headers.authorization.startsWith('Bearer')
 | 
			
		||||
  )
 | 
			
		||||
    token = req.headers.authorization.split(' ')[1]
 | 
			
		||||
  // Set token from cookie
 | 
			
		||||
  // else if (req.cookies.token) token = req.cookies.token
 | 
			
		||||
 | 
			
		||||
  // Make sure token exists
 | 
			
		||||
  if (!token)
 | 
			
		||||
    return next(new ErrorResponse('Not authorized to access this route', 401))
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    // Verify token
 | 
			
		||||
    const decoded = jwt.verify(token, Bun.env.ACCESS_TOKEN_SECRET)
 | 
			
		||||
 | 
			
		||||
    req.user = await User.findById(decoded.id)
 | 
			
		||||
 | 
			
		||||
    next()
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    return next(new ErrorResponse('Not authorized to access this route', 401))
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// Grants access to specific roles
 | 
			
		||||
export const authorize = (...roles) => {
 | 
			
		||||
  return (req, res, next) => {
 | 
			
		||||
    if (!roles.includes(req.user.role)) {
 | 
			
		||||
      return next(
 | 
			
		||||
          new ErrorResponse(
 | 
			
		||||
              `User role ${req.user.role} is not authorized to access this route.`,
 | 
			
		||||
              403
 | 
			
		||||
          )
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    next()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								middleware/error.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								middleware/error.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
import ErrorResponse from '../utils/errorResponse.js'
 | 
			
		||||
 | 
			
		||||
const errorHandler = (err, req, res, next) => {
 | 
			
		||||
  let error = { ...err }
 | 
			
		||||
  error.message = err.message
 | 
			
		||||
  console.log(err.stack.red)
 | 
			
		||||
 | 
			
		||||
  //Mongoose bad ObjectId
 | 
			
		||||
  if (err.name === 'CastError') {
 | 
			
		||||
    const message = `Resource not found${typeof(err.value) === 'string' ? ` with id of ${err.value}` : ''}`
 | 
			
		||||
    error = new ErrorResponse(message, 404)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //Mongoose duplicate key
 | 
			
		||||
  if (err.code === 11000) {
 | 
			
		||||
    const message = `Duplicate field value entered`
 | 
			
		||||
    error = new ErrorResponse(message, 400)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //Mongoose validation error
 | 
			
		||||
  if(err.name === 'ValidationError') {
 | 
			
		||||
    const message = Object.values(err.errors).map(val => val.message)
 | 
			
		||||
    error = new ErrorResponse(message, 400)
 | 
			
		||||
  }
 | 
			
		||||
  res.status(error.statusCode || 500).json({
 | 
			
		||||
    success: false,
 | 
			
		||||
    error: error.message || 'Server Error',
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default errorHandler
 | 
			
		||||
		Reference in New Issue
	
	Block a user