diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..700829616 Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3ec544c7a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.env \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 000000000..7b81738c2 --- /dev/null +++ b/app.js @@ -0,0 +1,1756 @@ +require('dotenv').config() + +const express = require("express"); +const bodyParser = require("body-parser"); +const ejs = require("ejs"); +const mongoose = require("mongoose"); +const md5 = require('md5'); +const session = require('express-session'); +const jwt = require('jsonwebtoken'); +const nodemailer = require('nodemailer'); +const passport = require("passport"); +const GoogleStrategy = require('passport-google-oauth20').Strategy; +const findOrCreate = require('mongoose-findorcreate'); +const methodOverride = require('method-override'); +const multer = require('multer') +const path = require('path'); +const fs = require('fs'); + +const app = express(); + +const cors=require("cors"); +const { Router } = require('express'); +const corsOptions ={ + origin:'*', + credentials:true, + optionSuccessStatus:200, +} + +/* Multer Setup for Images */ + +var storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, 'uploads/') + }, + filename: function (req, file, cb) { + console.log(file); + cb(null, file.originalname); + } +}) + +var upload = multer({ + storage: storage, + limits: { + fileSize: 1024 * 1024 + } +}) + +/* End multer Setup for Images */ + +app.use(cors(corsOptions)); + +mongoose.set('strictQuery', false); + +// for mac +// mongoose.connect("mongodb://localhost:27017/allPurdueDB"); + +// for windows + mongoose.connect("mongodb://127.0.0.1:27017/allPurdueDB"); + +const transporter = nodemailer.createTransport({ + service: 'gmail', + host: 'smtp.gmail.com', + port: 465, + secure: true, + auth: { + user: 'allpurdue2023@gmail.com', + pass: process.env.APP_PASS, + } +}); + +app.use(methodOverride('_method')); + +app.use(express.static("public")); +app.use(express.static("uploads")); + +app.set("view engine", "ejs"); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json()); +app.use(session({ + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: true +})); + +app.use(passport.initialize()); +app.use(passport.session()); + +/* ---------- [Start] Models ---------- */ + +const Schema = mongoose.Schema; + +// User Schema +const userSchema = new Schema( + { + name: { + type: String, + required: true, + }, + email: { + type: String, + lowercase: true, + unique: true, + validate: { + validator: validateEmail, + message: () => "Email address already registered.", + }, + required: [true, "User email required"], + }, + password: { + type: String, + }, + postedReviews: [{ + type: Schema.Types.ObjectId, + ref: 'Review', + }], + savedPlaces: [{ + type: Schema.Types.ObjectId, + ref: 'Place', + }], + savedBlogs: [{ + type: Schema.Types.ObjectId, + ref: 'Blog', + }], + savedPlaces: [{ + type: Schema.Types.ObjectId, + ref: 'Place', + }], + googleId: { + type: String + } + }, + { timestamps: true } +); + +userSchema.plugin(findOrCreate); + +async function validateEmail(email) { + const user = await User.findOne({ email }); + if (user) { + if (User.id === user.id) { + return true; + } + return false; + } + return true; +} + +const User = new mongoose.model("User", userSchema); + +let currentUser = null; + +// Place Schema + +const placeSchema = new Schema( + { + name: { + type: String, + required: true, + }, + description: { + type: String, + required: true, + }, + hours: [{ + type: String, + required: true, + }], + phone: { + type: String, + required: true, + }, + placeType: { + type: String, + required: true, + }, + tags: [ + { + type: String, + }, + ], + location: { + // Reference - https://mongoosejs.com/docs/geojson.html + type: { + type: String, + enum: ["Point"], + required: true, + }, + coordinates: { + type: [Number], + required: true, + }, + }, + reviews: [{ + type: Schema.Types.ObjectId, + ref: 'Review', + }], + images: [ + { + type: String, + }, + ], + address: { + type: String, + required: true, + }, + googleMap: { + type: String, + required: true, + }, + website: { + type: String, + required: true, + }, + featured: { + type: Boolean, + default: false, + } + }, + { timestamps: true } +); + +const Place = new mongoose.model("Place", placeSchema); + +// Anpther virtual to find number of ratings for each place +// Usage example - +// const doc = await Band.findOne({ name: 'Motley Crue' }).populate('numMembers'); +// doc.numMembers; // 2 + +// placeSchema.virtual('numRatings', { +// ref: 'Review', +// localField: 'name', +// foreignField: 'place', +// count: true +// }) + +// Reviews Schema + +const reviewSchema = new Schema( + { + rating: { + type: Number, + minimum: [1, "Minimum rating of 1 is required"], + maximum: [5, "Maximum rating of 5 is required"], + required: true, + }, + text: { + type: String, + }, + place: { + type: Schema.Types.ObjectId, + ref: 'Place', + required: true, + }, + author: { + type: Schema.Types.ObjectId, + ref: 'User', + required: true, + }, + likes: { + type: Number, + default: 0, + }, + likes_by: [{ + type: Schema.Types.ObjectId, + ref: 'User', + }], + reported: { + type: Boolean, + default: false, + }, + reported_by: [ + { + type: Schema.Types.ObjectId, + ref: "User", + }, + ], + }, + { timestamps: true } + ); + +const Review = new mongoose.model("Review", reviewSchema); + + +// Blogs Schema + +const blogSchema = new Schema( + { + title: { + type: String, + required: true, + }, + text: { + type: String, + }, + tags: [ + { + type: String, + }, + ], + author: { + type: Schema.Types.ObjectId, + ref: 'User', + required: true, + }, + images: [ + { + type: String, + }, + ], + likes: { + type: Number, + default: 0, + }, + likes_by: [{ + type: Schema.Types.ObjectId, + ref: 'User', + }], + reported: { + type: Boolean, + default: false, + }, + reported_by: [ + { + type: Schema.Types.ObjectId, + ref: "User", + }, + ], + featured: { + type: Boolean, + default: false, + } + }, + { timestamps: true } +); + +Blog = new mongoose.model("Blog", blogSchema); + +/* ---------- [End] Models ---------- */ + +/* ---------- [Start] Google Auth ---------- */ + +passport.serializeUser(function(user, done) { + done(null, user.id); +}); + +passport.deserializeUser(function(id, done) { + User.findById(id, function(err, user) { + done(err, user); + }); +}); + +passport.use(new GoogleStrategy({ + clientID: process.env.CLIENT_ID, + clientSecret: process.env.CLIENT_SECRET, + callbackURL: "http://localhost:3000/auth/google/allpurdue", + scope: ['profile', 'email'] +}, +function(accessToken, refreshToken, profile, cb) { + User.findOrCreate({ googleId: profile.id, name: profile.displayName, email: profile.emails[0].value }, function (err, user) { + if(err) { + console.log(err); + } + currentUser = user; + return cb(err, user); + }); +} +)); + +/* ---------- [End] Google Auth ---------- */ + +/* ---------- [Start] User Routes ---------- */ + +// GET route for retrieving all user data +app.get('/users', async (req, res) => { + try { + const users = await User.find(); + res.json(users); + } catch (err) { + console.error(err); + res.status(500).send( 'Error retrieving user data' ); + } +}); + +// GET route for getting specific user +app.get('/users/:id', async (req, res) => { + try { + const user = await User.findById(req.params.id); + res.send(user) + } catch (error) { + console.error(error); + } +}); + +/* ---------- [End] User Routes ---------- */ + +/* ---------- [Start] Place Routes ---------- */ + +/** +app.get('/add_place', (req, res) => { + res.render("add_place"); +}); + +// for adding a place +app.post('/add_place', async (req, res) => { + try { + const { name, description, placeType, latitude, longitude } = req.body; + + // Create a new Place object + const place = new Place({ + name, + description, + placeType, + location: { + type: 'Point', + coordinates: [latitude, longitude], + }, + }); + + // Save the Place object to the database + await place.save(); + res.render('landing') + + } catch (err) { + console.error(err); + res.status(500).send('Error adding place'); + } +}); +*/ + +// GET route for displaying all places +app.get('/places', async (req, res) => { + try { + const places = await Place.find({}); + const placesWithAvgRating = await Promise.all(places.map(async (place) => { + // Add average rating functionality + const reviews = await Review.find({ place: place._id }); + const totalRatings = reviews.reduce((acc, review) => acc + review.rating, 0); + const numReviews = reviews.length > 0 ? reviews.length : 0; + const avgRating = reviews.length > 0 ? roundToNearestHalf(totalRatings / reviews.length) : 0; + return { ...place._doc, avgRating, numReviews }; + })); + // sending places as response + res.send(placesWithAvgRating) + // res.render('all_places', { places: placesWithAvgRating }); + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}); + + +// GET route for displaying a single place by ID +app.get('/places/:id', async (req, res) => { + try { + const place = await Place.findById(req.params.id).populate('reviews'); + // Add average rating functionality + const reviewRatings = place.reviews.map(review => review.rating); + const averageRating = roundToNearestHalf(reviewRatings.reduce((acc, curr) => acc + curr, 0) / reviewRatings.length); + + // suggestions + const tags = place.tags; + const suggestedPlaces = await Place.find({tags:{$in:tags}, _id:{$ne: place._id}}).limit(5); + // if there aren't 5 places with matching tags then we check for matching placeType + if (suggestedPlaces.length < 5) { + const matchingPlaceTypes = await Place.find({placeType: place.placeType, _id:{$ne: place._id}}).limit(5 - suggestedPlaces.length); + suggestedPlaces.push(...matchingPlaceTypes); + } + + const hours = place.hours; + const phone = place.phone; + const website = place.website; + const address = place.address; + const googleMap = place.googleMap; + const reviews = place.reviews; + + // sending everything as response + const data = { place, reviewRatings, averageRating, suggestedPlaces, hours, phone, website, address, googleMap, reviews }; + res.send(data) + //res.render('place-details', { place: place, averageRating: averageRating, numRatings: reviewRatings.length }); + } catch (error) { + console.error(error); + // res.redirect('/'); + res.status(400).send("Error retrieving place data") + } +}); + +// // for modifying a place +// app.post('/modify/:place_id', (req, res) => { +// Place.findById(req.params.id) +// .then((place) =>{ +// place.name = req.body.name; +// place.description = req.body.description; +// place.placeType = req.body.placeType; +// place.tags = req.body.tags; +// place.location = req.body.location; +// newPlace.save() +// .then(() => res.json('Place Modified')) +// .catch((err) => console.log(err)); +// }) +// .catch((err) => console.log(err)); +// }); + +app.delete('/places/delete/:place_id', (req, res) => { + console.log("testing") + User.updateMany( + { savedPlaces: req.params.place_id }, + { $pull: { savedPlaces: req.params.place_id } }, + (err, result) => { + if (err) { + console.log(err); + } else { + console.log("Deleted place from users' savedPlaces:", result.nModified); + } + } + ); + + Place.findByIdAndDelete(req.params.place_id, (err) => { + if (err) { + console.log(err); + } else { + res.status(201).send('place deleted') + console.log("Place deleted"); + } + }); +}); + +// Save/Unsave specific place by ID +app.post('/places/:placeId/save-place/:author', async (req, res) => { + try { + const { placeId, author } = req.params; + const currentUser = await User.findOne({ _id: author }); + if (!currentUser) { + res.send("no user found"); + return; + } + if (currentUser.savedPlaces.includes(placeId)) { + currentUser.savedPlaces.pop(placeId); + res.status(201).send(currentUser.savedPlaces) + } else { + currentUser.savedPlaces.push(placeId); + } + await User.findOneAndUpdate( + { _id: currentUser._id }, + { savedPlaces: currentUser.savedPlaces } + ); + } catch (err) { + console.log(err); + } +}); + +// Save specific place by ID +app.post('/save-place/:id', async (req, res) => { + try { + if (!currentUser) { + res.send("no user logged in"); + return; + } + + if (currentUser.savedPlaces.includes(req.params.id)) { + // Remove the place from savedPlaces + const index = currentUser.savedPlaces.indexOf(req.params.id); + currentUser.savedPlaces.splice(index, 1); + } else { + currentUser.savedPlaces.push(req.params.id); + } + + User.findOneAndUpdate( + { _id: currentUser._id }, + { savedPlaces: currentUser.savedPlaces }, + { new: true }, + function (err, updatedUser) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + res.status(201).send(updatedUser); + } + } + ); + + } catch (err) { + console.log(err); + res.send('Error saving place'); + } +}); + + +// Get saved places of the current user +app.get('/saved-places/', async (req, res) => { + try { + if (!currentUser) { + res.send('user not logged in'); + return; + } + res.send(currentUser.savedPlaces); + } catch (err) { + console.log(err); + res.send('Error sending saved blogs'); + } +}); + +/* ---------- [End] Place Routes ---------- */ + +/* ---------- [Start] Search Routes ---------- */ + +// Set up the search endpoint route to handle GET requests +app.get('/search', async (req, res) => { + try { + //const query = req.query.q || ''; + const results = []; // No results to show yet + //res.render('search', { results }); + res.render('search', { results, query: null }); + } catch (err) { + console.error(err); + res.render('error', { message: 'An error has occurred' }); + } +}); + +// Set up the search endpoint to handle POST requests +app.post('/search' , async (req, res) => { + const query = req.body.query; + const regexQuery = new RegExp(query, 'i'); + // console.log(regexQuery); + try { + const results = await Place.find({ + $or: [ + { name: regexQuery }, + { placeType: regexQuery }, + { tags: regexQuery }, + ] + }).select('name placeType tags images').limit(3); + console.log(results); + res.send(results) + // if (req.xhr) { + // res.json({results }); // Return JSON containing the autocomplete suggestions + // } else { + // res.render('search', { results }); + // } + } catch (err) { + console.error(err); + // res.render('error', { message: 'An error has occurred' }); + } +}); + +/* ---------- [End] search Routes ---------- */ + +/* ---------- [Start] Reviews Routes ---------- */ + +// getting recent reviews +app.get('/recent-reviews', async (req, res) => { + try { + const recentReviews = await Review.find({}) + .populate('place', 'name tags placeType images') + .sort({ updatedAt: -1 }) + .exec() + + // sending recentReviews as response + res.send(recentReviews) + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}) + +// Add a review +app.post('/places/:placeId/reviews', async (req, res) => { + const placeId = req.params.placeId; + const rating = req.body.rating; + const text = req.body.review; + const author = req.body.author; // assuming user ID is stored in session + + try { + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + if (rating === null || rating < 1) { + return res.status(400).send('Please select rating of at least 1') + } + + const review = new Review({ + rating, + text, + place: placeId, + author + }); + + await review.save(); + + place.reviews.push(review); + await place.save(); + + res.status(201).json(review); + + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// edit review +app.get('/places/:place_id/reviews/:id/edit', async (req, res) => { + const reviewId = req.params.id; + const placeId = req.params.place_id; + // const userId = req.session.userId; // assuming user ID is stored in session + + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + // if (review.author.toString() !== userId) { + // return res.status(403).send('Unauthorized'); + // } + + res.render('edit-review', { review, place }); + } catch (error) { + console.error(error); + res.status(500).send('Server error'); + } +}); + +app.put('/places/:place_id/reviews/:id', async (req, res) => { + const reviewId = req.params.id; + const placeId = req.params.place_id; + // const userId = req.session.userId; // assuming user ID is stored in session + const { rating, text } = req.body; + + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + // if (review.author.toString() !== userId) { + // return res.status(403).send('Unauthorized'); + // } + + review.rating = rating; + review.text = text; + await review.save(); + + res.redirect(`/places/${review.place}`); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// delete review +app.delete('/places/:place_id/reviews/:id', async (req, res) => { + const reviewId = req.params.id; + const placeId = req.params.place_id; + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + await Review.findByIdAndDelete(reviewId); + + await Place.findByIdAndUpdate( + placeId, + { $pull: { reviews: reviewId } }, + { new: true } + ); + + res.redirect(`/places/${placeId}`); + + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Like and unlike a review +app.post('/reviews/:reviewId/like/:userId', async (req, res) => { + const reviewId = req.params.reviewId; + const user = await User.findById(req.params.userId); + + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const hasLiked = review.likes_by.includes(user._id); + + if (hasLiked) { + review.likes = review.likes - 1; + review.likes_by.pop(user._id); + // user.likedReviews.pop(review._id); + } else { + review.likes = review.likes + 1; + review.likes_by.push(user._id); + // user.likedReviews.push(review._id); + } + + // Save the changes to the review and user documents + await review.save(); + // await user.save(); + + return res.json({ review }); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Report a review +app.post("/reviews/report", async (req, res) => { + const { reviewId, userId } = req.body; + + try { + // Update the review document + const review = await Review.findByIdAndUpdate( + reviewId, + { reported: true, $addToSet: { reported_by: userId } }, + { new: true } + ); + + // Get the place and author of the review + const place = await Place.findById(review.place); + const author = await User.findById(review.author); + const authorName = author.name; + const reviewText = review.text; + const placeName = place.name; + + // Send email to the admin of the review + const adminEmail = "allpurdue2023@gmail.com"; + const msg1 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'A review on AllPurdue has been reported', + text: 'A review: ' + reviewText + ', \n for' + placeName + 'by' + authorName +'on AllPurdue has been reported.' + } + transporter.sendMail(msg1, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + // Send email to the author of the review + const authorEmail = author.email; + const msg2 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'Your review on AllPurdue has been reported', + text: 'Your review ' + reviewText + ', \n for' + placeName + ' on AllPurdue has been reported' + } + transporter.sendMail(msg2, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + res.status(200).json({ message: "Review reported successfully" }); + } catch (error) { + res.status(500).json({ message: "Error reporting review", error: error.message }); + } +}); + + +/* ---------- [End] Reviews Routes ---------- */ + +/* ---------- [Start] Admin Modification Routes ---------- */ + +//Get request to display add place form +app.get('/admin/add-place', async (req, res) => { + try { + res.render('add_place'); // render the add-place EJS view + } catch (err) { + console.log(err); + res.send('Error fetching add place form.') + } +}); + +//POST Route to add a place +app.post('/admin/add-place', async (req, res) => { + try { + console.log(req.body) + const newPlace = new Place({ + name: req.body.name, + description: req.body.description, + placeType: req.body.placeType, + hours: req.body.hours, + phone: req.body.phone, + tags: req.body.tags, + location: { + type: "Point", + coordinates: [req.body.longitude, req.body.latitude], + }, + images: req.body.images, + address: req.body.address, + googleMap: req.body.googleMap, + website: req.body.website + }); + await newPlace.save(); + res.status(201).send(newPlace._id); + } catch (err) { + console.log(err); + res.send("Error adding new place"); + } +}); + +// GET request to display the form to update a place +app.get('/admin/places/:id/edit', async (req, res) => { + try { + const place = await Place.findById(req.params.id); + if (!place) { + return res.status(404).send('Place not found'); + } + res.render('edit_place', { place }); + } catch (err) { + console.error(err); + res.send('Error getting edit place form'); + } +}); + +// PUT request to update a place +app.put('/admin/places/:id/edit', async (req, res) => { + try { + const place = await Place.findById(req.params.id); + if (!place) { + return res.status(404).send('Place not found'); + } + place.name = req.body.name; + place.description = req.body.description; + place.hours = req.body.hours; + place.phone = req.body.phone; + place.placeType = req.body.placeType; + place.tags = req.body.tags; + place.location = { + type: 'Point', + coordinates: [req.body.lng, req.body.lat], + }; + place.images = req.body.images; + place.address = req.body.address; + place.googleMap = req.body.googleMap; + place.website = req.body.website; + await place.save(); + res.status(201).send("place edited") + } catch (err) { + console.error(err); + res.send('Error editing place information'); + } +}); + +// DELETE a place by ID +app.delete('/admin/places/:id', async (req, res) => { + try { + const place = await Place.findById(req.params.id); + if (!place) { + return res.status(404).send('Place not found'); + } + await Place.findByIdAndDelete(req.params.id); + res.redirect('/admin/places'); + } catch (err) { + console.error(err); + res.send('Error Deleting Place'); + } +}); + +// delete any review +app.delete('/admin/places/:placeId/reviews/:reviewId', async (req, res) => { + try { + const review = await Review.findById(req.params.reviewId); + if (!review) { + return res.status(404).send('Review not found'); + } + await Review.findByIdAndDelete(req.params.reviewId); + const place = await Place.findById(req.params.placeId); + if (!place) { + return res.status(404).send('Place not found'); + } + place.reviews.pull(req.params.reviewId); + await place.save(); + res.redirect(`/admin/places/${req.params.placeId}`); + + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// DELETE specific blog by ID +app.delete('/admin/blogs/:id', async (req, res) => { + try { + User.updateMany( + { savedBlogs: req.params.id }, + { $pull: { savedBlogs: req.params.id } }, + (err, result) => { + if (err) { + console.log(err); + } else { + console.log("Deleted blog from users' savedBlogs:", result.nModified); + } + } + ); + await Blog.findByIdAndDelete(req.params.id); + res.status(201).send('blog deleted'); + } catch (err) { + console.log(err); + res.send('Error deleting blog'); + } +}); + +// Getting a featured place/blog +app.get('/featured', async (req, res) => { + try { + var place = await Place.findOne({ featured: true }).populate('reviews'); + + if (place) { + const reviewRatings = place.reviews.map(review => review.rating); + const averageRating = roundToNearestHalf(reviewRatings.reduce((acc, curr) => acc + curr, 0) / reviewRatings.length); + place = { ...place.toObject(), averageRating }; + console.log(place) + } + + const blog = await Blog.findOne({ featured: true }); + + res.status(200).json({ place, blog }); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + + + +// Admin Feature/Unfeature Place +app.post('/feature-place/:placeId', async (req, res) => { + const placeId = req.params.placeId; + + try { + await Place.updateMany({ _id: { $ne: placeId } }, { $set: { featured: false } }); + await Blog.updateMany({ _id: { $ne: placeId } }, { $set: { featured: false } }); + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + place.featured = true; + await place.save(); + + res.status(201).send(place) + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Admin Feature/Unfeature Blog +app.post('/feature-blog/:blogId', async (req, res) => { + const blogId = req.params.blogId; + + try { + await Place.updateMany({ _id: { $ne: blogId } }, { $set: { featured: false } }); + await Blog.updateMany({ _id: { $ne: blogId } }, { $set: { featured: false } }); + + const blog = await Blog.findById(blogId); + + if (!blog) { + return res.status(404).send('Blog not found'); + } + + blog.featured = true; + + await blog.save(); + + res.status(201).send(blog); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + + +/* ---------- [End] Admin Modification Routes ---------- */ + + +/* ---------- [Start] Blogs Routes ---------- */ + +// GET all blogs +app.get('/blogs', async (req, res) => { + try { + const blogs = await Blog.find(); + res.json(blogs); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}); + +// GET new blog form +app.get('/blogs/new-blog', async (req, res) => { + try { + const users = await User.find(); + res.render('new-blog', { users }); + } catch (err) { + console.log(err); + res.send('Error retrieving new blog form'); + } +}); + +// POST new blog +app.post('/blogs', upload.array('blog-images'), async (req, res) => { + try { + const { title, text, author, tags } = req.body; + const images = req.files.map((file) => file.filename); + + const newBlog = new Blog({ title, text, author, tags, images }); + const savedBlog = await newBlog.save(); + + res.status(201).send(savedBlog); + } catch (err) { + console.error(err); + res.status(500).send('Server error'); + } +}); + +// GET specific blog by ID +app.get('/blogs/:id', async (req, res) => { + try { + const blog = await Blog.findById(req.params.id); + const user = await User.findById(blog.author); + const response = { + ...blog.toObject(), + authorName: user.name + }; + res.send(response); + } catch (err) { + console.log(err); + res.send('Error retrieving blog'); + } +}); + +// DELETE specific blog by ID +app.delete('/blogs/:id', async (req, res) => { + try { + User.updateMany( + { savedBlogs: req.params.id }, + { $pull: { savedBlogs: req.params.id } }, + (err, result) => { + if (err) { + console.log(err); + } else { + console.log("Deleted blog from users' savedBlogs:", result.nModified); + } + } + ); + const blog = await Blog.findById(req.params.id); + var img = blog.images; + for(var i = 0; i < img.length; i++) { + var path = "uploads/" + img[i]; + fs.unlink(path, (err) => { + if (err) throw err; + }); + } + const blogAuthor = blog.author + const blogTitle = blog.title + let authorEmail = '' + await Blog.findByIdAndDelete(req.params.id); + User.findOne({ _id: blogAuthor }, (err, user) => { + if (err) { + console.log(err); + } else if (!user) { + } else { + authorEmail = user.email + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'Your blog ' + blogTitle + ' on AllPurdue has been deleted', + text: 'Your blog ' + blogTitle + ' on AllPurdue has been deleted' + } + transporter.sendMail(msg, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + res.status(201).send('blog deleted'); + } + }); +} catch(err) { + console.log(err) +}}); + +// Save/Unsave specific blog by ID +app.post('/save-blog/:id', async (req, res) => { + try { + if (!currentUser) { + res.send("no user logged in"); + return; + } + + const index = currentUser.savedBlogs.indexOf(req.params.id); + if (index > -1) { + currentUser.savedBlogs.splice(index, 1); + } else { + currentUser.savedBlogs.push(req.params.id); + } + + User.findOneAndUpdate( + { _id: currentUser._id }, + { savedBlogs: currentUser.savedBlogs }, + function (err) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + res.status(201).send("success"); + } + } + ); + } catch (err) { + console.log(err); + res.send('Error saving blogs'); + } +}); + +// Get saved blogs of the current user +app.get('/saved-blogs/', async (req, res) => { + try { + if (!currentUser) { + res.send('user not logged in'); + return; + } + res.send(currentUser.savedBlogs); + } catch (err) { + console.log(err); + res.send('Error sending saved blogs'); + } +}); + +// GET recent blogs +app.get('/recent-blogs', async (req, res) => { + try { + const recentBlogs = await Blog.find({}) + // .populate('place', 'name tags placeType images') + // @Andrew / @Antony Add Whatever You Need In this Populate Command ^ + .sort({ updatedAt: -1 }) + .exec() + + // sending recentBlogs as response + res.send(recentBlogs) + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}) + +// PUT update an existing blog by ID +app.put("/blogs/:blogId", async (req, res) => { + const { blogId } = req.params; + const { title, text, places } = req.body; + try { + const blog = await Blog.findByIdAndUpdate( + blogId, + { title, text, places }, + { new: true } + ); + if (!blog) { + return res.status(404).json({ message: "Blog not found" }); + } + res.json(blog); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server Error" }); + } +}); + +// Like and unlike a blog +app.post('/blogs/:blogId/like/:userId', async (req, res) => { + const blogId = req.params.blogId; + const user = await User.findById(req.params.userId); + + try { + const blog = await Blog.findById(blogId); + + if (!blog) { + return res.status(404).send('Blog not found'); + } + + const hasLiked = blog.likes_by.includes(user._id); + + if (hasLiked) { + blog.likes = blog.likes - 1; + blog.likes_by.pop(user._id); + // user.likedReviews.pop(review._id); + } else { + blog.likes = blog.likes + 1; + blog.likes_by.push(user._id); + // user.likedReviews.push(review._id); + } + + // Save the changes to the blog and user documents + await blog.save(); + // await user.save(); + + return res.send(blog); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Report a blog +app.post("/blog/report", async (req, res) => { + const { blogId, userId } = req.body; + + try { + // Update the blog document + const blog = await Blog.findByIdAndUpdate( + blogId, + { reported: true, $addToSet: { reported_by: userId } }, + { new: true } + ); + + // Get the place and author of the blog + const title = await Place.findById(blog.title); + const author = await User.findById(blog.author); + const authorName = author.name; + + // Send email to the admin of the review + const adminEmail = "allpurdue2023@gmail.com"; + const msg1 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: adminEmail, + subject: 'A blog on AllPurdue has been reported', + text: 'A blog: ' + title + ', \n by' + authorName +'on AllPurdue has been reported.' + } + transporter.sendMail(msg1, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + // Send email to the author of the review + const authorEmail = author.email; + const msg2 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'Your blog on AllPurdue has been reported', + text: 'Your blog ' + title + ' on AllPurdue has been reported' + } + transporter.sendMail(msg2, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + res.status(200).json({ message: "Review reported successfully" }); + } catch (error) { + res.status(500).json({ message: "Error reporting review", error: error.message }); + } +}); + +/* ---------- [End] Blogs Routes ----------- */ + +/* ---------- [Start] Login/Register/Home/Forgot Password Routes ---------- */ + +// GET Route for Homes +app.get('/', function (req, res) { + res.render("home"); +}); + +app.get("/auth/google", + passport.authenticate('google', { scope: ["profile", "email"] }) +); + +app.get("/auth/google/allpurdue", + passport.authenticate('google', { failureRedirect: "http://localhost:3001/loginsignup" }), + function(req, res, err) { + console.log("google login") + console.log(res.data) + res.redirect("http://localhost:3001/"); + }); + +// GET Route for Register +app.get('/register', function (req, res) { + res.render("register"); +}) + +// GET Route for Landing +app.get('/landing', function (req, res) { + if (!req.session.user && !req.isAuthenticated()) { + res.render("login"); + } else { + res.render("landing"); + } +}) + +// POST Route for Register +app.post('/register', function (req, res) { + console.log(req.body); + if(req.body.password.length < 6) { + console.log("Password length is less than 6!"); + res.status(500).send('Password length is less than 6!'); + return; + } + if (req.body.username.endsWith('@purdue.edu')) { + const link = `http://localhost:3000/verify-user/${req.body.name}/${req.body.username}/${md5(req.body.password)}`; + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: req.body.username, + subject: 'AllPurdue Email Verification', + text: `Hello from AllPurdue! Boiler Up! Please click the link to verify your email:\n${link}.\n` + } + transporter.sendMail(msg, function(error, info){ + if (error) { + console.log(error); + } else { + console.log('Email sent: ' + info.response); + } + }); + res.render('home'); + } else { + const newUser = new User({ + name: req.body.name, + email: req.body.username, + password: md5(req.body.password) + }); + newUser.save(function (err) { + if (err) { + console.log(err); + res.status(500).send('Unsuccessful registration'); + } else { + console.log("User Successfully Registered!"); + res.render("login"); + } + }); + } +}); + +app.get("/verify-user/:name/:email/:password", function(req, res) { + console.log("User Successfully Verified!"); + const { name, email, password } = req.params; + const newUser = new User({ + name: name, + email: email, + password: password + }); + newUser.save(function (err) { + if (err) { + console.log(err); + res.status(500).send('Unsuccessful registration'); + } else { + console.log("User Successfully Registered!"); + res.render("login"); + } + }); +}); + +let adminAccount = false + +// POST Route for Login +app.post('/login', function (req, res) { + var username = req.body.username; + var userPass = md5(req.body.password); + + User.findOne({ email: username }, function (err, userExists) { + if (err) { + console.log(err); + res.status(500).send('User not found') + } else { + if (userExists) { + if (userExists.password === userPass) { + console.log("User Successfully Logged In!"); + // sessionStorage.setItem('userID', userExists._id); + req.session.user = userExists; + currentUser = userExists; + + if (currentUser.email === 'allpurdue2023@gmail.com') { + adminAccount = true; + } + + res.redirect("/landing"); + } else { + console.log("Incorrect Password!"); + res.status(500).send("Incorrect Password") + } + } else { + console.log("User not found!"); + res.status(500).send('Email does not exist') + } + } + } + ); +}); + +// GET Route for currentUser +app.get("/currentUser", function (req, res) { + res.send(currentUser); +}); + +// GET Route for /forgotPassword +app.get("/forgotPassword", function (req, res) { + res.render("forgotPassword"); +}); + +global.resetUser; + +// POST Route for /forgotPassword +app.post("/forgotPassword", function (req, res) { + const userEmail = req.body.username; + User.findOne({ email: userEmail }, function (err, userExists) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + if (userExists) { + const secret = process.env.JWT_SECRET + userExists.password; + const payload = { + email: userEmail, + id: userExists.id + }; + const token = jwt.sign(payload, secret, { expiresIn: '15m' }); + resetUser = userExists; + const link = `http://localhost:3000/reset-password/${userExists.id}/${token}`; + console.log(userEmail); + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: userEmail, + subject: 'AllPurdue Password Reset', + text: `Hello from AllPurdue! Boiler Up! Please click the link to reset your email:\n${link}.\n The link is only valid for 15 minutes.` + } + transporter.sendMail(msg, function(error, info){ + if (error) { + console.log(error); + } else { + console.log('Email sent: ' + info.response); + } + }); + console.log("Link Sent Successfuly! Check your inbox!"); + res.redirect('/'); + } else { + console.log("User not found!"); + res.redirect("/forgotPassword"); + } + } + }); +}); + +app.get("/reset-password/:id/:token", async function (req, res) { + const { id, token } = req.params; + if (id === global.resetUser.id) { + const user = await User.findOne({email: global.resetUser.email}); + const secret = process.env.JWT_SECRET + user.password; + try { + jwt.verify(token, secret); + console.log("Verified"); + res.render("reset-password", {email: global.resetUser.email}); + } catch (error) { + console.log(error); + } + } else { + console.log("Verification Failed"); + res.sendStatus(500); + } +}); + +app.post("/reset-password", function (req, res) { + const update = { + password: md5(req.body.password) + }; + const filter = { + email: global.resetUser.email + } + User.findOneAndUpdate(filter, update, function(err) { + if(err) { + console.log(err) + } else { + console.log("Password Updated Successfully!"); + res.redirect("/login"); + } + }) +}); + +// edit password from user dashboard page +app.post("/change-password", function (req, res) { + User.findOne({ password: md5(req.body.currentPassword) }, function (err, user) { + if (err) { + console.log(err); + res.sendStatus(500); + } else if (!user) { + console.log("Current password does not match"); + res.status(400).send("Current password does not match"); + } else { + User.findOneAndUpdate({ _id: req.body.userId }, { password: md5(req.body.newPassword) }, function (err) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + res.status(201).send("success") + } + }); + } + }); +}); + +app.get("/logout", async function(req, res) { + req.session.user = null; + currentUser = null + adminAccount = false + req.logout(function(err) { + if (err) { return next(err); } + res.redirect('/'); + }); +}); + +// GET Route for Login +app.get('/login', function (req, res) { + res.render("login"); +}) + +// GET Route for Landing +app.get('/landing', function (req, res) { + res.render("landing"); +}) + +app.listen(3000, function () { + console.log("Server started at Port 3000!"); +}); + +/* ---------- [End] Login/Register/Home/Forgot Password Routes ---------- */ + +/* ---------- [Start] Helper Functions ---------- */ + +function roundToNearestHalf(num) { + // Check if the number is within the desired range + if (num < 0 || num > 5) { + throw new Error('Number is not within the range of 0 and 5'); + } + + // Round to the nearest 0.5 + const roundedNum = Math.round(num * 2) / 2; + + // Ensure the rounded number is within the range of 0 and 5 + return Math.max(0, Math.min(5, roundedNum)); +} + +/* ---------- [End] Helper Functions ---------- */ + +// contact page submission +app.post("/submit-request", function (req, res) { + console.log("getting here") + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: '"Team AllPurdue" allpurdue2023@gmail.com', + subject: req.body.requestType + ' request from ' + req.body.email, + text: req.body.requestType === 'Add Place' ? req.body.name + '\n' + req.body.message : 'Report Issue' + '\n' + req.body.message + } + transporter.sendMail(msg, function(err){ + if (err) { + console.log(err); + res.status(500).send("error submitting request") + } else { + console.log("successful request submission"); + res.status(200).send("success") + } + }); +}); + +// sharing post request +app.post("/share", function (req, res) { + const id = req.body.email; + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: id, + subject: 'AllPurdue Referral', + text: 'Hello! This is a referral' + } + transporter.sendMail(msg, function(err){ + if (err) { + console.log(err); + res.status(500).send("error submitting request") + } else { + console.log("successful request submission"); + res.status(200).send("success") + } + }); +}); + +// verifying if account is admin account +app.get("/verify-admin", function (req, res) { + if (adminAccount) { + res.send(true) + } +}) + +app.get('/trending-places', async (req, res) => { + try { + const places = await Place.find({}); + const placesWithNumReviews = await Promise.all(places.map(async (place) => { + const reviews = await Review.find({ place: place._id }); + return { ...place._doc, numReviews: reviews.length }; + })); + const sortedPlaces = placesWithNumReviews.sort((a, b) => b.numReviews - a.numReviews); + const trendingPlaces = await Promise.all(sortedPlaces.map(async (place) => { + const reviews = await Review.find({ place: place._id }); + const totalRatings = reviews.reduce((acc, review) => acc + review.rating, 0); + const avgRating = reviews.length > 0 ? roundToNearestHalf(totalRatings / reviews.length) : 0; + return { ...place, avgRating }; + })); + res.send(trendingPlaces); + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}); diff --git a/backend/.env b/backend/.env new file mode 100644 index 000000000..e9e1c5084 --- /dev/null +++ b/backend/.env @@ -0,0 +1,5 @@ +SESSION_SECRET=secretuwgfugeurvrtrr +JWT_SECRET=yaudbwedubfuebufbashbayash +APP_PASS=tmwwstrgjmofdiuz +CLIENT_ID=1022274060459-lq1e7425n3qua3ujvgaqbfm44rn4hba4.apps.googleusercontent.com +CLIENT_SECRET=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 \ No newline at end of file diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 000000000..6229a60e0 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,25 @@ +# dependencies +/node_modules +/.pnp +.pnp.js + +# package lock +!**/package-lock.json + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 000000000..9c384fc28 --- /dev/null +++ b/backend/README.md @@ -0,0 +1 @@ +# AllPurdue \ No newline at end of file diff --git a/backend/app.js b/backend/app.js new file mode 100644 index 000000000..7b81738c2 --- /dev/null +++ b/backend/app.js @@ -0,0 +1,1756 @@ +require('dotenv').config() + +const express = require("express"); +const bodyParser = require("body-parser"); +const ejs = require("ejs"); +const mongoose = require("mongoose"); +const md5 = require('md5'); +const session = require('express-session'); +const jwt = require('jsonwebtoken'); +const nodemailer = require('nodemailer'); +const passport = require("passport"); +const GoogleStrategy = require('passport-google-oauth20').Strategy; +const findOrCreate = require('mongoose-findorcreate'); +const methodOverride = require('method-override'); +const multer = require('multer') +const path = require('path'); +const fs = require('fs'); + +const app = express(); + +const cors=require("cors"); +const { Router } = require('express'); +const corsOptions ={ + origin:'*', + credentials:true, + optionSuccessStatus:200, +} + +/* Multer Setup for Images */ + +var storage = multer.diskStorage({ + destination: function (req, file, cb) { + cb(null, 'uploads/') + }, + filename: function (req, file, cb) { + console.log(file); + cb(null, file.originalname); + } +}) + +var upload = multer({ + storage: storage, + limits: { + fileSize: 1024 * 1024 + } +}) + +/* End multer Setup for Images */ + +app.use(cors(corsOptions)); + +mongoose.set('strictQuery', false); + +// for mac +// mongoose.connect("mongodb://localhost:27017/allPurdueDB"); + +// for windows + mongoose.connect("mongodb://127.0.0.1:27017/allPurdueDB"); + +const transporter = nodemailer.createTransport({ + service: 'gmail', + host: 'smtp.gmail.com', + port: 465, + secure: true, + auth: { + user: 'allpurdue2023@gmail.com', + pass: process.env.APP_PASS, + } +}); + +app.use(methodOverride('_method')); + +app.use(express.static("public")); +app.use(express.static("uploads")); + +app.set("view engine", "ejs"); +app.use(bodyParser.urlencoded({ extended: true })); +app.use(bodyParser.json()); +app.use(session({ + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: true +})); + +app.use(passport.initialize()); +app.use(passport.session()); + +/* ---------- [Start] Models ---------- */ + +const Schema = mongoose.Schema; + +// User Schema +const userSchema = new Schema( + { + name: { + type: String, + required: true, + }, + email: { + type: String, + lowercase: true, + unique: true, + validate: { + validator: validateEmail, + message: () => "Email address already registered.", + }, + required: [true, "User email required"], + }, + password: { + type: String, + }, + postedReviews: [{ + type: Schema.Types.ObjectId, + ref: 'Review', + }], + savedPlaces: [{ + type: Schema.Types.ObjectId, + ref: 'Place', + }], + savedBlogs: [{ + type: Schema.Types.ObjectId, + ref: 'Blog', + }], + savedPlaces: [{ + type: Schema.Types.ObjectId, + ref: 'Place', + }], + googleId: { + type: String + } + }, + { timestamps: true } +); + +userSchema.plugin(findOrCreate); + +async function validateEmail(email) { + const user = await User.findOne({ email }); + if (user) { + if (User.id === user.id) { + return true; + } + return false; + } + return true; +} + +const User = new mongoose.model("User", userSchema); + +let currentUser = null; + +// Place Schema + +const placeSchema = new Schema( + { + name: { + type: String, + required: true, + }, + description: { + type: String, + required: true, + }, + hours: [{ + type: String, + required: true, + }], + phone: { + type: String, + required: true, + }, + placeType: { + type: String, + required: true, + }, + tags: [ + { + type: String, + }, + ], + location: { + // Reference - https://mongoosejs.com/docs/geojson.html + type: { + type: String, + enum: ["Point"], + required: true, + }, + coordinates: { + type: [Number], + required: true, + }, + }, + reviews: [{ + type: Schema.Types.ObjectId, + ref: 'Review', + }], + images: [ + { + type: String, + }, + ], + address: { + type: String, + required: true, + }, + googleMap: { + type: String, + required: true, + }, + website: { + type: String, + required: true, + }, + featured: { + type: Boolean, + default: false, + } + }, + { timestamps: true } +); + +const Place = new mongoose.model("Place", placeSchema); + +// Anpther virtual to find number of ratings for each place +// Usage example - +// const doc = await Band.findOne({ name: 'Motley Crue' }).populate('numMembers'); +// doc.numMembers; // 2 + +// placeSchema.virtual('numRatings', { +// ref: 'Review', +// localField: 'name', +// foreignField: 'place', +// count: true +// }) + +// Reviews Schema + +const reviewSchema = new Schema( + { + rating: { + type: Number, + minimum: [1, "Minimum rating of 1 is required"], + maximum: [5, "Maximum rating of 5 is required"], + required: true, + }, + text: { + type: String, + }, + place: { + type: Schema.Types.ObjectId, + ref: 'Place', + required: true, + }, + author: { + type: Schema.Types.ObjectId, + ref: 'User', + required: true, + }, + likes: { + type: Number, + default: 0, + }, + likes_by: [{ + type: Schema.Types.ObjectId, + ref: 'User', + }], + reported: { + type: Boolean, + default: false, + }, + reported_by: [ + { + type: Schema.Types.ObjectId, + ref: "User", + }, + ], + }, + { timestamps: true } + ); + +const Review = new mongoose.model("Review", reviewSchema); + + +// Blogs Schema + +const blogSchema = new Schema( + { + title: { + type: String, + required: true, + }, + text: { + type: String, + }, + tags: [ + { + type: String, + }, + ], + author: { + type: Schema.Types.ObjectId, + ref: 'User', + required: true, + }, + images: [ + { + type: String, + }, + ], + likes: { + type: Number, + default: 0, + }, + likes_by: [{ + type: Schema.Types.ObjectId, + ref: 'User', + }], + reported: { + type: Boolean, + default: false, + }, + reported_by: [ + { + type: Schema.Types.ObjectId, + ref: "User", + }, + ], + featured: { + type: Boolean, + default: false, + } + }, + { timestamps: true } +); + +Blog = new mongoose.model("Blog", blogSchema); + +/* ---------- [End] Models ---------- */ + +/* ---------- [Start] Google Auth ---------- */ + +passport.serializeUser(function(user, done) { + done(null, user.id); +}); + +passport.deserializeUser(function(id, done) { + User.findById(id, function(err, user) { + done(err, user); + }); +}); + +passport.use(new GoogleStrategy({ + clientID: process.env.CLIENT_ID, + clientSecret: process.env.CLIENT_SECRET, + callbackURL: "http://localhost:3000/auth/google/allpurdue", + scope: ['profile', 'email'] +}, +function(accessToken, refreshToken, profile, cb) { + User.findOrCreate({ googleId: profile.id, name: profile.displayName, email: profile.emails[0].value }, function (err, user) { + if(err) { + console.log(err); + } + currentUser = user; + return cb(err, user); + }); +} +)); + +/* ---------- [End] Google Auth ---------- */ + +/* ---------- [Start] User Routes ---------- */ + +// GET route for retrieving all user data +app.get('/users', async (req, res) => { + try { + const users = await User.find(); + res.json(users); + } catch (err) { + console.error(err); + res.status(500).send( 'Error retrieving user data' ); + } +}); + +// GET route for getting specific user +app.get('/users/:id', async (req, res) => { + try { + const user = await User.findById(req.params.id); + res.send(user) + } catch (error) { + console.error(error); + } +}); + +/* ---------- [End] User Routes ---------- */ + +/* ---------- [Start] Place Routes ---------- */ + +/** +app.get('/add_place', (req, res) => { + res.render("add_place"); +}); + +// for adding a place +app.post('/add_place', async (req, res) => { + try { + const { name, description, placeType, latitude, longitude } = req.body; + + // Create a new Place object + const place = new Place({ + name, + description, + placeType, + location: { + type: 'Point', + coordinates: [latitude, longitude], + }, + }); + + // Save the Place object to the database + await place.save(); + res.render('landing') + + } catch (err) { + console.error(err); + res.status(500).send('Error adding place'); + } +}); +*/ + +// GET route for displaying all places +app.get('/places', async (req, res) => { + try { + const places = await Place.find({}); + const placesWithAvgRating = await Promise.all(places.map(async (place) => { + // Add average rating functionality + const reviews = await Review.find({ place: place._id }); + const totalRatings = reviews.reduce((acc, review) => acc + review.rating, 0); + const numReviews = reviews.length > 0 ? reviews.length : 0; + const avgRating = reviews.length > 0 ? roundToNearestHalf(totalRatings / reviews.length) : 0; + return { ...place._doc, avgRating, numReviews }; + })); + // sending places as response + res.send(placesWithAvgRating) + // res.render('all_places', { places: placesWithAvgRating }); + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}); + + +// GET route for displaying a single place by ID +app.get('/places/:id', async (req, res) => { + try { + const place = await Place.findById(req.params.id).populate('reviews'); + // Add average rating functionality + const reviewRatings = place.reviews.map(review => review.rating); + const averageRating = roundToNearestHalf(reviewRatings.reduce((acc, curr) => acc + curr, 0) / reviewRatings.length); + + // suggestions + const tags = place.tags; + const suggestedPlaces = await Place.find({tags:{$in:tags}, _id:{$ne: place._id}}).limit(5); + // if there aren't 5 places with matching tags then we check for matching placeType + if (suggestedPlaces.length < 5) { + const matchingPlaceTypes = await Place.find({placeType: place.placeType, _id:{$ne: place._id}}).limit(5 - suggestedPlaces.length); + suggestedPlaces.push(...matchingPlaceTypes); + } + + const hours = place.hours; + const phone = place.phone; + const website = place.website; + const address = place.address; + const googleMap = place.googleMap; + const reviews = place.reviews; + + // sending everything as response + const data = { place, reviewRatings, averageRating, suggestedPlaces, hours, phone, website, address, googleMap, reviews }; + res.send(data) + //res.render('place-details', { place: place, averageRating: averageRating, numRatings: reviewRatings.length }); + } catch (error) { + console.error(error); + // res.redirect('/'); + res.status(400).send("Error retrieving place data") + } +}); + +// // for modifying a place +// app.post('/modify/:place_id', (req, res) => { +// Place.findById(req.params.id) +// .then((place) =>{ +// place.name = req.body.name; +// place.description = req.body.description; +// place.placeType = req.body.placeType; +// place.tags = req.body.tags; +// place.location = req.body.location; +// newPlace.save() +// .then(() => res.json('Place Modified')) +// .catch((err) => console.log(err)); +// }) +// .catch((err) => console.log(err)); +// }); + +app.delete('/places/delete/:place_id', (req, res) => { + console.log("testing") + User.updateMany( + { savedPlaces: req.params.place_id }, + { $pull: { savedPlaces: req.params.place_id } }, + (err, result) => { + if (err) { + console.log(err); + } else { + console.log("Deleted place from users' savedPlaces:", result.nModified); + } + } + ); + + Place.findByIdAndDelete(req.params.place_id, (err) => { + if (err) { + console.log(err); + } else { + res.status(201).send('place deleted') + console.log("Place deleted"); + } + }); +}); + +// Save/Unsave specific place by ID +app.post('/places/:placeId/save-place/:author', async (req, res) => { + try { + const { placeId, author } = req.params; + const currentUser = await User.findOne({ _id: author }); + if (!currentUser) { + res.send("no user found"); + return; + } + if (currentUser.savedPlaces.includes(placeId)) { + currentUser.savedPlaces.pop(placeId); + res.status(201).send(currentUser.savedPlaces) + } else { + currentUser.savedPlaces.push(placeId); + } + await User.findOneAndUpdate( + { _id: currentUser._id }, + { savedPlaces: currentUser.savedPlaces } + ); + } catch (err) { + console.log(err); + } +}); + +// Save specific place by ID +app.post('/save-place/:id', async (req, res) => { + try { + if (!currentUser) { + res.send("no user logged in"); + return; + } + + if (currentUser.savedPlaces.includes(req.params.id)) { + // Remove the place from savedPlaces + const index = currentUser.savedPlaces.indexOf(req.params.id); + currentUser.savedPlaces.splice(index, 1); + } else { + currentUser.savedPlaces.push(req.params.id); + } + + User.findOneAndUpdate( + { _id: currentUser._id }, + { savedPlaces: currentUser.savedPlaces }, + { new: true }, + function (err, updatedUser) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + res.status(201).send(updatedUser); + } + } + ); + + } catch (err) { + console.log(err); + res.send('Error saving place'); + } +}); + + +// Get saved places of the current user +app.get('/saved-places/', async (req, res) => { + try { + if (!currentUser) { + res.send('user not logged in'); + return; + } + res.send(currentUser.savedPlaces); + } catch (err) { + console.log(err); + res.send('Error sending saved blogs'); + } +}); + +/* ---------- [End] Place Routes ---------- */ + +/* ---------- [Start] Search Routes ---------- */ + +// Set up the search endpoint route to handle GET requests +app.get('/search', async (req, res) => { + try { + //const query = req.query.q || ''; + const results = []; // No results to show yet + //res.render('search', { results }); + res.render('search', { results, query: null }); + } catch (err) { + console.error(err); + res.render('error', { message: 'An error has occurred' }); + } +}); + +// Set up the search endpoint to handle POST requests +app.post('/search' , async (req, res) => { + const query = req.body.query; + const regexQuery = new RegExp(query, 'i'); + // console.log(regexQuery); + try { + const results = await Place.find({ + $or: [ + { name: regexQuery }, + { placeType: regexQuery }, + { tags: regexQuery }, + ] + }).select('name placeType tags images').limit(3); + console.log(results); + res.send(results) + // if (req.xhr) { + // res.json({results }); // Return JSON containing the autocomplete suggestions + // } else { + // res.render('search', { results }); + // } + } catch (err) { + console.error(err); + // res.render('error', { message: 'An error has occurred' }); + } +}); + +/* ---------- [End] search Routes ---------- */ + +/* ---------- [Start] Reviews Routes ---------- */ + +// getting recent reviews +app.get('/recent-reviews', async (req, res) => { + try { + const recentReviews = await Review.find({}) + .populate('place', 'name tags placeType images') + .sort({ updatedAt: -1 }) + .exec() + + // sending recentReviews as response + res.send(recentReviews) + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}) + +// Add a review +app.post('/places/:placeId/reviews', async (req, res) => { + const placeId = req.params.placeId; + const rating = req.body.rating; + const text = req.body.review; + const author = req.body.author; // assuming user ID is stored in session + + try { + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + if (rating === null || rating < 1) { + return res.status(400).send('Please select rating of at least 1') + } + + const review = new Review({ + rating, + text, + place: placeId, + author + }); + + await review.save(); + + place.reviews.push(review); + await place.save(); + + res.status(201).json(review); + + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// edit review +app.get('/places/:place_id/reviews/:id/edit', async (req, res) => { + const reviewId = req.params.id; + const placeId = req.params.place_id; + // const userId = req.session.userId; // assuming user ID is stored in session + + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + // if (review.author.toString() !== userId) { + // return res.status(403).send('Unauthorized'); + // } + + res.render('edit-review', { review, place }); + } catch (error) { + console.error(error); + res.status(500).send('Server error'); + } +}); + +app.put('/places/:place_id/reviews/:id', async (req, res) => { + const reviewId = req.params.id; + const placeId = req.params.place_id; + // const userId = req.session.userId; // assuming user ID is stored in session + const { rating, text } = req.body; + + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + // if (review.author.toString() !== userId) { + // return res.status(403).send('Unauthorized'); + // } + + review.rating = rating; + review.text = text; + await review.save(); + + res.redirect(`/places/${review.place}`); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// delete review +app.delete('/places/:place_id/reviews/:id', async (req, res) => { + const reviewId = req.params.id; + const placeId = req.params.place_id; + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + await Review.findByIdAndDelete(reviewId); + + await Place.findByIdAndUpdate( + placeId, + { $pull: { reviews: reviewId } }, + { new: true } + ); + + res.redirect(`/places/${placeId}`); + + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Like and unlike a review +app.post('/reviews/:reviewId/like/:userId', async (req, res) => { + const reviewId = req.params.reviewId; + const user = await User.findById(req.params.userId); + + try { + const review = await Review.findById(reviewId); + + if (!review) { + return res.status(404).send('Review not found'); + } + + const hasLiked = review.likes_by.includes(user._id); + + if (hasLiked) { + review.likes = review.likes - 1; + review.likes_by.pop(user._id); + // user.likedReviews.pop(review._id); + } else { + review.likes = review.likes + 1; + review.likes_by.push(user._id); + // user.likedReviews.push(review._id); + } + + // Save the changes to the review and user documents + await review.save(); + // await user.save(); + + return res.json({ review }); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Report a review +app.post("/reviews/report", async (req, res) => { + const { reviewId, userId } = req.body; + + try { + // Update the review document + const review = await Review.findByIdAndUpdate( + reviewId, + { reported: true, $addToSet: { reported_by: userId } }, + { new: true } + ); + + // Get the place and author of the review + const place = await Place.findById(review.place); + const author = await User.findById(review.author); + const authorName = author.name; + const reviewText = review.text; + const placeName = place.name; + + // Send email to the admin of the review + const adminEmail = "allpurdue2023@gmail.com"; + const msg1 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'A review on AllPurdue has been reported', + text: 'A review: ' + reviewText + ', \n for' + placeName + 'by' + authorName +'on AllPurdue has been reported.' + } + transporter.sendMail(msg1, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + // Send email to the author of the review + const authorEmail = author.email; + const msg2 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'Your review on AllPurdue has been reported', + text: 'Your review ' + reviewText + ', \n for' + placeName + ' on AllPurdue has been reported' + } + transporter.sendMail(msg2, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + res.status(200).json({ message: "Review reported successfully" }); + } catch (error) { + res.status(500).json({ message: "Error reporting review", error: error.message }); + } +}); + + +/* ---------- [End] Reviews Routes ---------- */ + +/* ---------- [Start] Admin Modification Routes ---------- */ + +//Get request to display add place form +app.get('/admin/add-place', async (req, res) => { + try { + res.render('add_place'); // render the add-place EJS view + } catch (err) { + console.log(err); + res.send('Error fetching add place form.') + } +}); + +//POST Route to add a place +app.post('/admin/add-place', async (req, res) => { + try { + console.log(req.body) + const newPlace = new Place({ + name: req.body.name, + description: req.body.description, + placeType: req.body.placeType, + hours: req.body.hours, + phone: req.body.phone, + tags: req.body.tags, + location: { + type: "Point", + coordinates: [req.body.longitude, req.body.latitude], + }, + images: req.body.images, + address: req.body.address, + googleMap: req.body.googleMap, + website: req.body.website + }); + await newPlace.save(); + res.status(201).send(newPlace._id); + } catch (err) { + console.log(err); + res.send("Error adding new place"); + } +}); + +// GET request to display the form to update a place +app.get('/admin/places/:id/edit', async (req, res) => { + try { + const place = await Place.findById(req.params.id); + if (!place) { + return res.status(404).send('Place not found'); + } + res.render('edit_place', { place }); + } catch (err) { + console.error(err); + res.send('Error getting edit place form'); + } +}); + +// PUT request to update a place +app.put('/admin/places/:id/edit', async (req, res) => { + try { + const place = await Place.findById(req.params.id); + if (!place) { + return res.status(404).send('Place not found'); + } + place.name = req.body.name; + place.description = req.body.description; + place.hours = req.body.hours; + place.phone = req.body.phone; + place.placeType = req.body.placeType; + place.tags = req.body.tags; + place.location = { + type: 'Point', + coordinates: [req.body.lng, req.body.lat], + }; + place.images = req.body.images; + place.address = req.body.address; + place.googleMap = req.body.googleMap; + place.website = req.body.website; + await place.save(); + res.status(201).send("place edited") + } catch (err) { + console.error(err); + res.send('Error editing place information'); + } +}); + +// DELETE a place by ID +app.delete('/admin/places/:id', async (req, res) => { + try { + const place = await Place.findById(req.params.id); + if (!place) { + return res.status(404).send('Place not found'); + } + await Place.findByIdAndDelete(req.params.id); + res.redirect('/admin/places'); + } catch (err) { + console.error(err); + res.send('Error Deleting Place'); + } +}); + +// delete any review +app.delete('/admin/places/:placeId/reviews/:reviewId', async (req, res) => { + try { + const review = await Review.findById(req.params.reviewId); + if (!review) { + return res.status(404).send('Review not found'); + } + await Review.findByIdAndDelete(req.params.reviewId); + const place = await Place.findById(req.params.placeId); + if (!place) { + return res.status(404).send('Place not found'); + } + place.reviews.pull(req.params.reviewId); + await place.save(); + res.redirect(`/admin/places/${req.params.placeId}`); + + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// DELETE specific blog by ID +app.delete('/admin/blogs/:id', async (req, res) => { + try { + User.updateMany( + { savedBlogs: req.params.id }, + { $pull: { savedBlogs: req.params.id } }, + (err, result) => { + if (err) { + console.log(err); + } else { + console.log("Deleted blog from users' savedBlogs:", result.nModified); + } + } + ); + await Blog.findByIdAndDelete(req.params.id); + res.status(201).send('blog deleted'); + } catch (err) { + console.log(err); + res.send('Error deleting blog'); + } +}); + +// Getting a featured place/blog +app.get('/featured', async (req, res) => { + try { + var place = await Place.findOne({ featured: true }).populate('reviews'); + + if (place) { + const reviewRatings = place.reviews.map(review => review.rating); + const averageRating = roundToNearestHalf(reviewRatings.reduce((acc, curr) => acc + curr, 0) / reviewRatings.length); + place = { ...place.toObject(), averageRating }; + console.log(place) + } + + const blog = await Blog.findOne({ featured: true }); + + res.status(200).json({ place, blog }); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + + + +// Admin Feature/Unfeature Place +app.post('/feature-place/:placeId', async (req, res) => { + const placeId = req.params.placeId; + + try { + await Place.updateMany({ _id: { $ne: placeId } }, { $set: { featured: false } }); + await Blog.updateMany({ _id: { $ne: placeId } }, { $set: { featured: false } }); + + const place = await Place.findById(placeId); + + if (!place) { + return res.status(404).send('Place not found'); + } + + place.featured = true; + await place.save(); + + res.status(201).send(place) + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Admin Feature/Unfeature Blog +app.post('/feature-blog/:blogId', async (req, res) => { + const blogId = req.params.blogId; + + try { + await Place.updateMany({ _id: { $ne: blogId } }, { $set: { featured: false } }); + await Blog.updateMany({ _id: { $ne: blogId } }, { $set: { featured: false } }); + + const blog = await Blog.findById(blogId); + + if (!blog) { + return res.status(404).send('Blog not found'); + } + + blog.featured = true; + + await blog.save(); + + res.status(201).send(blog); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + + +/* ---------- [End] Admin Modification Routes ---------- */ + + +/* ---------- [Start] Blogs Routes ---------- */ + +// GET all blogs +app.get('/blogs', async (req, res) => { + try { + const blogs = await Blog.find(); + res.json(blogs); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}); + +// GET new blog form +app.get('/blogs/new-blog', async (req, res) => { + try { + const users = await User.find(); + res.render('new-blog', { users }); + } catch (err) { + console.log(err); + res.send('Error retrieving new blog form'); + } +}); + +// POST new blog +app.post('/blogs', upload.array('blog-images'), async (req, res) => { + try { + const { title, text, author, tags } = req.body; + const images = req.files.map((file) => file.filename); + + const newBlog = new Blog({ title, text, author, tags, images }); + const savedBlog = await newBlog.save(); + + res.status(201).send(savedBlog); + } catch (err) { + console.error(err); + res.status(500).send('Server error'); + } +}); + +// GET specific blog by ID +app.get('/blogs/:id', async (req, res) => { + try { + const blog = await Blog.findById(req.params.id); + const user = await User.findById(blog.author); + const response = { + ...blog.toObject(), + authorName: user.name + }; + res.send(response); + } catch (err) { + console.log(err); + res.send('Error retrieving blog'); + } +}); + +// DELETE specific blog by ID +app.delete('/blogs/:id', async (req, res) => { + try { + User.updateMany( + { savedBlogs: req.params.id }, + { $pull: { savedBlogs: req.params.id } }, + (err, result) => { + if (err) { + console.log(err); + } else { + console.log("Deleted blog from users' savedBlogs:", result.nModified); + } + } + ); + const blog = await Blog.findById(req.params.id); + var img = blog.images; + for(var i = 0; i < img.length; i++) { + var path = "uploads/" + img[i]; + fs.unlink(path, (err) => { + if (err) throw err; + }); + } + const blogAuthor = blog.author + const blogTitle = blog.title + let authorEmail = '' + await Blog.findByIdAndDelete(req.params.id); + User.findOne({ _id: blogAuthor }, (err, user) => { + if (err) { + console.log(err); + } else if (!user) { + } else { + authorEmail = user.email + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'Your blog ' + blogTitle + ' on AllPurdue has been deleted', + text: 'Your blog ' + blogTitle + ' on AllPurdue has been deleted' + } + transporter.sendMail(msg, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + res.status(201).send('blog deleted'); + } + }); +} catch(err) { + console.log(err) +}}); + +// Save/Unsave specific blog by ID +app.post('/save-blog/:id', async (req, res) => { + try { + if (!currentUser) { + res.send("no user logged in"); + return; + } + + const index = currentUser.savedBlogs.indexOf(req.params.id); + if (index > -1) { + currentUser.savedBlogs.splice(index, 1); + } else { + currentUser.savedBlogs.push(req.params.id); + } + + User.findOneAndUpdate( + { _id: currentUser._id }, + { savedBlogs: currentUser.savedBlogs }, + function (err) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + res.status(201).send("success"); + } + } + ); + } catch (err) { + console.log(err); + res.send('Error saving blogs'); + } +}); + +// Get saved blogs of the current user +app.get('/saved-blogs/', async (req, res) => { + try { + if (!currentUser) { + res.send('user not logged in'); + return; + } + res.send(currentUser.savedBlogs); + } catch (err) { + console.log(err); + res.send('Error sending saved blogs'); + } +}); + +// GET recent blogs +app.get('/recent-blogs', async (req, res) => { + try { + const recentBlogs = await Blog.find({}) + // .populate('place', 'name tags placeType images') + // @Andrew / @Antony Add Whatever You Need In this Populate Command ^ + .sort({ updatedAt: -1 }) + .exec() + + // sending recentBlogs as response + res.send(recentBlogs) + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}) + +// PUT update an existing blog by ID +app.put("/blogs/:blogId", async (req, res) => { + const { blogId } = req.params; + const { title, text, places } = req.body; + try { + const blog = await Blog.findByIdAndUpdate( + blogId, + { title, text, places }, + { new: true } + ); + if (!blog) { + return res.status(404).json({ message: "Blog not found" }); + } + res.json(blog); + } catch (error) { + console.error(error); + res.status(500).json({ message: "Server Error" }); + } +}); + +// Like and unlike a blog +app.post('/blogs/:blogId/like/:userId', async (req, res) => { + const blogId = req.params.blogId; + const user = await User.findById(req.params.userId); + + try { + const blog = await Blog.findById(blogId); + + if (!blog) { + return res.status(404).send('Blog not found'); + } + + const hasLiked = blog.likes_by.includes(user._id); + + if (hasLiked) { + blog.likes = blog.likes - 1; + blog.likes_by.pop(user._id); + // user.likedReviews.pop(review._id); + } else { + blog.likes = blog.likes + 1; + blog.likes_by.push(user._id); + // user.likedReviews.push(review._id); + } + + // Save the changes to the blog and user documents + await blog.save(); + // await user.save(); + + return res.send(blog); + } catch (error) { + console.log(error); + res.status(500).send('Server error'); + } +}); + +// Report a blog +app.post("/blog/report", async (req, res) => { + const { blogId, userId } = req.body; + + try { + // Update the blog document + const blog = await Blog.findByIdAndUpdate( + blogId, + { reported: true, $addToSet: { reported_by: userId } }, + { new: true } + ); + + // Get the place and author of the blog + const title = await Place.findById(blog.title); + const author = await User.findById(blog.author); + const authorName = author.name; + + // Send email to the admin of the review + const adminEmail = "allpurdue2023@gmail.com"; + const msg1 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: adminEmail, + subject: 'A blog on AllPurdue has been reported', + text: 'A blog: ' + title + ', \n by' + authorName +'on AllPurdue has been reported.' + } + transporter.sendMail(msg1, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + // Send email to the author of the review + const authorEmail = author.email; + const msg2 = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: authorEmail, + subject: 'Your blog on AllPurdue has been reported', + text: 'Your blog ' + title + ' on AllPurdue has been reported' + } + transporter.sendMail(msg2, function(err){ + if (err) { + console.log(err); + res.status(500).send("error emailing deletion confirmation email") + } else { + console.log("successful deletion email sent"); + res.status(200).send("success") + } + }); + + res.status(200).json({ message: "Review reported successfully" }); + } catch (error) { + res.status(500).json({ message: "Error reporting review", error: error.message }); + } +}); + +/* ---------- [End] Blogs Routes ----------- */ + +/* ---------- [Start] Login/Register/Home/Forgot Password Routes ---------- */ + +// GET Route for Homes +app.get('/', function (req, res) { + res.render("home"); +}); + +app.get("/auth/google", + passport.authenticate('google', { scope: ["profile", "email"] }) +); + +app.get("/auth/google/allpurdue", + passport.authenticate('google', { failureRedirect: "http://localhost:3001/loginsignup" }), + function(req, res, err) { + console.log("google login") + console.log(res.data) + res.redirect("http://localhost:3001/"); + }); + +// GET Route for Register +app.get('/register', function (req, res) { + res.render("register"); +}) + +// GET Route for Landing +app.get('/landing', function (req, res) { + if (!req.session.user && !req.isAuthenticated()) { + res.render("login"); + } else { + res.render("landing"); + } +}) + +// POST Route for Register +app.post('/register', function (req, res) { + console.log(req.body); + if(req.body.password.length < 6) { + console.log("Password length is less than 6!"); + res.status(500).send('Password length is less than 6!'); + return; + } + if (req.body.username.endsWith('@purdue.edu')) { + const link = `http://localhost:3000/verify-user/${req.body.name}/${req.body.username}/${md5(req.body.password)}`; + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: req.body.username, + subject: 'AllPurdue Email Verification', + text: `Hello from AllPurdue! Boiler Up! Please click the link to verify your email:\n${link}.\n` + } + transporter.sendMail(msg, function(error, info){ + if (error) { + console.log(error); + } else { + console.log('Email sent: ' + info.response); + } + }); + res.render('home'); + } else { + const newUser = new User({ + name: req.body.name, + email: req.body.username, + password: md5(req.body.password) + }); + newUser.save(function (err) { + if (err) { + console.log(err); + res.status(500).send('Unsuccessful registration'); + } else { + console.log("User Successfully Registered!"); + res.render("login"); + } + }); + } +}); + +app.get("/verify-user/:name/:email/:password", function(req, res) { + console.log("User Successfully Verified!"); + const { name, email, password } = req.params; + const newUser = new User({ + name: name, + email: email, + password: password + }); + newUser.save(function (err) { + if (err) { + console.log(err); + res.status(500).send('Unsuccessful registration'); + } else { + console.log("User Successfully Registered!"); + res.render("login"); + } + }); +}); + +let adminAccount = false + +// POST Route for Login +app.post('/login', function (req, res) { + var username = req.body.username; + var userPass = md5(req.body.password); + + User.findOne({ email: username }, function (err, userExists) { + if (err) { + console.log(err); + res.status(500).send('User not found') + } else { + if (userExists) { + if (userExists.password === userPass) { + console.log("User Successfully Logged In!"); + // sessionStorage.setItem('userID', userExists._id); + req.session.user = userExists; + currentUser = userExists; + + if (currentUser.email === 'allpurdue2023@gmail.com') { + adminAccount = true; + } + + res.redirect("/landing"); + } else { + console.log("Incorrect Password!"); + res.status(500).send("Incorrect Password") + } + } else { + console.log("User not found!"); + res.status(500).send('Email does not exist') + } + } + } + ); +}); + +// GET Route for currentUser +app.get("/currentUser", function (req, res) { + res.send(currentUser); +}); + +// GET Route for /forgotPassword +app.get("/forgotPassword", function (req, res) { + res.render("forgotPassword"); +}); + +global.resetUser; + +// POST Route for /forgotPassword +app.post("/forgotPassword", function (req, res) { + const userEmail = req.body.username; + User.findOne({ email: userEmail }, function (err, userExists) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + if (userExists) { + const secret = process.env.JWT_SECRET + userExists.password; + const payload = { + email: userEmail, + id: userExists.id + }; + const token = jwt.sign(payload, secret, { expiresIn: '15m' }); + resetUser = userExists; + const link = `http://localhost:3000/reset-password/${userExists.id}/${token}`; + console.log(userEmail); + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: userEmail, + subject: 'AllPurdue Password Reset', + text: `Hello from AllPurdue! Boiler Up! Please click the link to reset your email:\n${link}.\n The link is only valid for 15 minutes.` + } + transporter.sendMail(msg, function(error, info){ + if (error) { + console.log(error); + } else { + console.log('Email sent: ' + info.response); + } + }); + console.log("Link Sent Successfuly! Check your inbox!"); + res.redirect('/'); + } else { + console.log("User not found!"); + res.redirect("/forgotPassword"); + } + } + }); +}); + +app.get("/reset-password/:id/:token", async function (req, res) { + const { id, token } = req.params; + if (id === global.resetUser.id) { + const user = await User.findOne({email: global.resetUser.email}); + const secret = process.env.JWT_SECRET + user.password; + try { + jwt.verify(token, secret); + console.log("Verified"); + res.render("reset-password", {email: global.resetUser.email}); + } catch (error) { + console.log(error); + } + } else { + console.log("Verification Failed"); + res.sendStatus(500); + } +}); + +app.post("/reset-password", function (req, res) { + const update = { + password: md5(req.body.password) + }; + const filter = { + email: global.resetUser.email + } + User.findOneAndUpdate(filter, update, function(err) { + if(err) { + console.log(err) + } else { + console.log("Password Updated Successfully!"); + res.redirect("/login"); + } + }) +}); + +// edit password from user dashboard page +app.post("/change-password", function (req, res) { + User.findOne({ password: md5(req.body.currentPassword) }, function (err, user) { + if (err) { + console.log(err); + res.sendStatus(500); + } else if (!user) { + console.log("Current password does not match"); + res.status(400).send("Current password does not match"); + } else { + User.findOneAndUpdate({ _id: req.body.userId }, { password: md5(req.body.newPassword) }, function (err) { + if (err) { + console.log(err); + res.sendStatus(500); + } else { + res.status(201).send("success") + } + }); + } + }); +}); + +app.get("/logout", async function(req, res) { + req.session.user = null; + currentUser = null + adminAccount = false + req.logout(function(err) { + if (err) { return next(err); } + res.redirect('/'); + }); +}); + +// GET Route for Login +app.get('/login', function (req, res) { + res.render("login"); +}) + +// GET Route for Landing +app.get('/landing', function (req, res) { + res.render("landing"); +}) + +app.listen(3000, function () { + console.log("Server started at Port 3000!"); +}); + +/* ---------- [End] Login/Register/Home/Forgot Password Routes ---------- */ + +/* ---------- [Start] Helper Functions ---------- */ + +function roundToNearestHalf(num) { + // Check if the number is within the desired range + if (num < 0 || num > 5) { + throw new Error('Number is not within the range of 0 and 5'); + } + + // Round to the nearest 0.5 + const roundedNum = Math.round(num * 2) / 2; + + // Ensure the rounded number is within the range of 0 and 5 + return Math.max(0, Math.min(5, roundedNum)); +} + +/* ---------- [End] Helper Functions ---------- */ + +// contact page submission +app.post("/submit-request", function (req, res) { + console.log("getting here") + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: '"Team AllPurdue" allpurdue2023@gmail.com', + subject: req.body.requestType + ' request from ' + req.body.email, + text: req.body.requestType === 'Add Place' ? req.body.name + '\n' + req.body.message : 'Report Issue' + '\n' + req.body.message + } + transporter.sendMail(msg, function(err){ + if (err) { + console.log(err); + res.status(500).send("error submitting request") + } else { + console.log("successful request submission"); + res.status(200).send("success") + } + }); +}); + +// sharing post request +app.post("/share", function (req, res) { + const id = req.body.email; + const msg = { + from: '"Team AllPurdue" allpurdue2023@gmail.com', + to: id, + subject: 'AllPurdue Referral', + text: 'Hello! This is a referral' + } + transporter.sendMail(msg, function(err){ + if (err) { + console.log(err); + res.status(500).send("error submitting request") + } else { + console.log("successful request submission"); + res.status(200).send("success") + } + }); +}); + +// verifying if account is admin account +app.get("/verify-admin", function (req, res) { + if (adminAccount) { + res.send(true) + } +}) + +app.get('/trending-places', async (req, res) => { + try { + const places = await Place.find({}); + const placesWithNumReviews = await Promise.all(places.map(async (place) => { + const reviews = await Review.find({ place: place._id }); + return { ...place._doc, numReviews: reviews.length }; + })); + const sortedPlaces = placesWithNumReviews.sort((a, b) => b.numReviews - a.numReviews); + const trendingPlaces = await Promise.all(sortedPlaces.map(async (place) => { + const reviews = await Review.find({ place: place._id }); + const totalRatings = reviews.reduce((acc, review) => acc + review.rating, 0); + const avgRating = reviews.length > 0 ? roundToNearestHalf(totalRatings / reviews.length) : 0; + return { ...place, avgRating }; + })); + res.send(trendingPlaces); + } catch (error) { + console.error(error); + res.status(500).send('Internal Server Error'); + } +}); diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 000000000..0dc845a3c --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,3824 @@ +{ + "name": "allpurdue", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "allpurdue", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@sendgrid/mail": "^7.7.0", + "bcrypt": "^5.1.0", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "ejs": "^3.1.8", + "express": "^4.18.2", + "express-session": "^1.17.3", + "fs": "^0.0.1-security", + "jsonwebtoken": "^9.0.0", + "md5": "^2.3.0", + "method-override": "^3.0.0", + "mongoose": "^6.9.1", + "mongoose-encryption": "^2.1.2", + "mongoose-findorcreate": "^3.0.0", + "multer": "^1.4.5-lts.1", + "nodemailer": "^6.9.1", + "nodemon": "^2.0.20", + "passport": "^0.6.0", + "passport-google-oauth20": "^2.0.0", + "passport-local": "^1.0.0", + "passport-local-mongoose": "^7.1.2", + "socket.io": "^4.6.1" + } + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "optional": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-sdk/abort-controller": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.272.0.tgz", + "integrity": "sha512-s2TV3phapcTwZNr4qLxbfuQuE9ZMP4RoJdkvRRCkKdm6jslsWLJf2Zlcxti/23hOlINUMYv2iXE2pftIgWGdpg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.272.0.tgz", + "integrity": "sha512-uMjRWcNvX7SoGaVn0mXWD43+Z1awPahQwGW3riDLfXHZdOgw2oFDhD3Jg5jQ8OzQLUfDvArhE3WyZwlS4muMuQ==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.272.0", + "@aws-sdk/config-resolver": "3.272.0", + "@aws-sdk/credential-provider-node": "3.272.0", + "@aws-sdk/fetch-http-handler": "3.272.0", + "@aws-sdk/hash-node": "3.272.0", + "@aws-sdk/invalid-dependency": "3.272.0", + "@aws-sdk/middleware-content-length": "3.272.0", + "@aws-sdk/middleware-endpoint": "3.272.0", + "@aws-sdk/middleware-host-header": "3.272.0", + "@aws-sdk/middleware-logger": "3.272.0", + "@aws-sdk/middleware-recursion-detection": "3.272.0", + "@aws-sdk/middleware-retry": "3.272.0", + "@aws-sdk/middleware-serde": "3.272.0", + "@aws-sdk/middleware-signing": "3.272.0", + "@aws-sdk/middleware-stack": "3.272.0", + "@aws-sdk/middleware-user-agent": "3.272.0", + "@aws-sdk/node-config-provider": "3.272.0", + "@aws-sdk/node-http-handler": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/smithy-client": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/url-parser": "3.272.0", + "@aws-sdk/util-base64": "3.208.0", + "@aws-sdk/util-body-length-browser": "3.188.0", + "@aws-sdk/util-body-length-node": "3.208.0", + "@aws-sdk/util-defaults-mode-browser": "3.272.0", + "@aws-sdk/util-defaults-mode-node": "3.272.0", + "@aws-sdk/util-endpoints": "3.272.0", + "@aws-sdk/util-retry": "3.272.0", + "@aws-sdk/util-user-agent-browser": "3.272.0", + "@aws-sdk/util-user-agent-node": "3.272.0", + "@aws-sdk/util-utf8": "3.254.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.272.0.tgz", + "integrity": "sha512-xn9a0IGONwQIARmngThoRhF1lLGjHAD67sUaShgIMaIMc6ipVYN6alWG1VuUpoUQ6iiwMEt0CHdfCyLyUV/fTA==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.272.0", + "@aws-sdk/fetch-http-handler": "3.272.0", + "@aws-sdk/hash-node": "3.272.0", + "@aws-sdk/invalid-dependency": "3.272.0", + "@aws-sdk/middleware-content-length": "3.272.0", + "@aws-sdk/middleware-endpoint": "3.272.0", + "@aws-sdk/middleware-host-header": "3.272.0", + "@aws-sdk/middleware-logger": "3.272.0", + "@aws-sdk/middleware-recursion-detection": "3.272.0", + "@aws-sdk/middleware-retry": "3.272.0", + "@aws-sdk/middleware-serde": "3.272.0", + "@aws-sdk/middleware-stack": "3.272.0", + "@aws-sdk/middleware-user-agent": "3.272.0", + "@aws-sdk/node-config-provider": "3.272.0", + "@aws-sdk/node-http-handler": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/smithy-client": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/url-parser": "3.272.0", + "@aws-sdk/util-base64": "3.208.0", + "@aws-sdk/util-body-length-browser": "3.188.0", + "@aws-sdk/util-body-length-node": "3.208.0", + "@aws-sdk/util-defaults-mode-browser": "3.272.0", + "@aws-sdk/util-defaults-mode-node": "3.272.0", + "@aws-sdk/util-endpoints": "3.272.0", + "@aws-sdk/util-retry": "3.272.0", + "@aws-sdk/util-user-agent-browser": "3.272.0", + "@aws-sdk/util-user-agent-node": "3.272.0", + "@aws-sdk/util-utf8": "3.254.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.272.0.tgz", + "integrity": "sha512-ECcXu3xoa1yggnGKMTh29eWNHiF/wC6r5Uqbla22eOOosyh0+Z6lkJ3JUSLOUKCkBXA4Cs/tJL9UDFBrKbSlvA==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.272.0", + "@aws-sdk/fetch-http-handler": "3.272.0", + "@aws-sdk/hash-node": "3.272.0", + "@aws-sdk/invalid-dependency": "3.272.0", + "@aws-sdk/middleware-content-length": "3.272.0", + "@aws-sdk/middleware-endpoint": "3.272.0", + "@aws-sdk/middleware-host-header": "3.272.0", + "@aws-sdk/middleware-logger": "3.272.0", + "@aws-sdk/middleware-recursion-detection": "3.272.0", + "@aws-sdk/middleware-retry": "3.272.0", + "@aws-sdk/middleware-serde": "3.272.0", + "@aws-sdk/middleware-stack": "3.272.0", + "@aws-sdk/middleware-user-agent": "3.272.0", + "@aws-sdk/node-config-provider": "3.272.0", + "@aws-sdk/node-http-handler": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/smithy-client": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/url-parser": "3.272.0", + "@aws-sdk/util-base64": "3.208.0", + "@aws-sdk/util-body-length-browser": "3.188.0", + "@aws-sdk/util-body-length-node": "3.208.0", + "@aws-sdk/util-defaults-mode-browser": "3.272.0", + "@aws-sdk/util-defaults-mode-node": "3.272.0", + "@aws-sdk/util-endpoints": "3.272.0", + "@aws-sdk/util-retry": "3.272.0", + "@aws-sdk/util-user-agent-browser": "3.272.0", + "@aws-sdk/util-user-agent-node": "3.272.0", + "@aws-sdk/util-utf8": "3.254.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.272.0.tgz", + "integrity": "sha512-kigxCxURp3WupufGaL/LABMb7UQfzAQkKcj9royizL3ItJ0vw5kW/JFrPje5IW1mfLgdPF7PI9ShOjE0fCLTqA==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/config-resolver": "3.272.0", + "@aws-sdk/credential-provider-node": "3.272.0", + "@aws-sdk/fetch-http-handler": "3.272.0", + "@aws-sdk/hash-node": "3.272.0", + "@aws-sdk/invalid-dependency": "3.272.0", + "@aws-sdk/middleware-content-length": "3.272.0", + "@aws-sdk/middleware-endpoint": "3.272.0", + "@aws-sdk/middleware-host-header": "3.272.0", + "@aws-sdk/middleware-logger": "3.272.0", + "@aws-sdk/middleware-recursion-detection": "3.272.0", + "@aws-sdk/middleware-retry": "3.272.0", + "@aws-sdk/middleware-sdk-sts": "3.272.0", + "@aws-sdk/middleware-serde": "3.272.0", + "@aws-sdk/middleware-signing": "3.272.0", + "@aws-sdk/middleware-stack": "3.272.0", + "@aws-sdk/middleware-user-agent": "3.272.0", + "@aws-sdk/node-config-provider": "3.272.0", + "@aws-sdk/node-http-handler": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/smithy-client": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/url-parser": "3.272.0", + "@aws-sdk/util-base64": "3.208.0", + "@aws-sdk/util-body-length-browser": "3.188.0", + "@aws-sdk/util-body-length-node": "3.208.0", + "@aws-sdk/util-defaults-mode-browser": "3.272.0", + "@aws-sdk/util-defaults-mode-node": "3.272.0", + "@aws-sdk/util-endpoints": "3.272.0", + "@aws-sdk/util-retry": "3.272.0", + "@aws-sdk/util-user-agent-browser": "3.272.0", + "@aws-sdk/util-user-agent-node": "3.272.0", + "@aws-sdk/util-utf8": "3.254.0", + "fast-xml-parser": "4.0.11", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/config-resolver": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.272.0.tgz", + "integrity": "sha512-Dr4CffRVNsOp3LRNdpvcH6XuSgXOSLblWliCy/5I86cNl567KVMxujVx6uPrdTXYs2h1rt3MNl6jQGnAiJeTbw==", + "optional": true, + "dependencies": { + "@aws-sdk/signature-v4": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/util-config-provider": "3.208.0", + "@aws-sdk/util-middleware": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.272.0.tgz", + "integrity": "sha512-rVx0rtQjbiYCM0nah2rB/2ut2YJYPpRr1AbW/Hd4r/PI+yiusrmXAwuT4HIW2yr34zsQMPi1jZ3WHN9Rn9mzlg==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.272.0.tgz", + "integrity": "sha512-QI65NbLnKLYHyTYhXaaUrq6eVsCCrMUb05WDA7+TJkWkjXesovpjc8vUKgFiLSxmgKmb2uOhHNcDyObKMrYQFw==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-imds": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.272.0.tgz", + "integrity": "sha512-wwAfVY1jTFQEfxVfdYD5r5ieYGl+0g4nhekVxNMqE8E1JeRDd18OqiwAflzpgBIqxfqvCUkf+vl5JYyacMkNAQ==", + "optional": true, + "dependencies": { + "@aws-sdk/node-config-provider": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/url-parser": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.272.0.tgz", + "integrity": "sha512-iE3CDzK5NcupHYjfYjBdY1JCy8NLEoRUsboEjG0i0gy3S3jVpDeVHX1dLVcL/slBFj6GiM7SoNV/UfKnJf3Gaw==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.272.0", + "@aws-sdk/credential-provider-imds": "3.272.0", + "@aws-sdk/credential-provider-process": "3.272.0", + "@aws-sdk/credential-provider-sso": "3.272.0", + "@aws-sdk/credential-provider-web-identity": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/shared-ini-file-loader": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.272.0.tgz", + "integrity": "sha512-FI8uvwM1IxiRSvbkdKv8DZG5vxU3ezaseTaB1fHWTxEUFb0pWIoHX9oeOKer9Fj31SOZTCNAaYFURbSRuZlm/w==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.272.0", + "@aws-sdk/credential-provider-imds": "3.272.0", + "@aws-sdk/credential-provider-ini": "3.272.0", + "@aws-sdk/credential-provider-process": "3.272.0", + "@aws-sdk/credential-provider-sso": "3.272.0", + "@aws-sdk/credential-provider-web-identity": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/shared-ini-file-loader": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.272.0.tgz", + "integrity": "sha512-hiCAjWWm2PeBFp5cjkxqyam/XADjiS+e7GzwC34TbZn3LisS0uoweLojj9tD11NnnUhyhbLteUvu5+rotOLwrg==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/shared-ini-file-loader": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.272.0.tgz", + "integrity": "sha512-hwYaulyiU/7chKKFecxCeo0ls6Dxs7h+5EtoYcJJGvfpvCncyOZF35t00OAsCd3Wo7HkhhgfpGdb6dmvCNQAZQ==", + "optional": true, + "dependencies": { + "@aws-sdk/client-sso": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/shared-ini-file-loader": "3.272.0", + "@aws-sdk/token-providers": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.272.0.tgz", + "integrity": "sha512-ImrHMkcgneGa/HadHAQXPwOrX26sAKuB8qlMxZF/ZCM2B55u8deY+ZVkVuraeKb7YsahMGehPFOfRAF6mvFI5Q==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.272.0.tgz", + "integrity": "sha512-ucd6Xq6aBMf+nM4uz5zkjL11mwaE5BV1Q4hkulaGu2v1dRA8n6zhLJk/sb4hOJ7leelqMJMErlbQ2T3MkYvlJQ==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.272.0", + "@aws-sdk/client-sso": "3.272.0", + "@aws-sdk/client-sts": "3.272.0", + "@aws-sdk/credential-provider-cognito-identity": "3.272.0", + "@aws-sdk/credential-provider-env": "3.272.0", + "@aws-sdk/credential-provider-imds": "3.272.0", + "@aws-sdk/credential-provider-ini": "3.272.0", + "@aws-sdk/credential-provider-node": "3.272.0", + "@aws-sdk/credential-provider-process": "3.272.0", + "@aws-sdk/credential-provider-sso": "3.272.0", + "@aws-sdk/credential-provider-web-identity": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/shared-ini-file-loader": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/fetch-http-handler": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.272.0.tgz", + "integrity": "sha512-1Qhm9e0RbS1Xf4CZqUbQyUMkDLd7GrsRXWIvm9b86/vgeV8/WnjO3CMue9D51nYgcyQORhYXv6uVjAYCWbUExA==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/querystring-builder": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/util-base64": "3.208.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/hash-node": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.272.0.tgz", + "integrity": "sha512-40dwND+iAm3VtPHPZu7/+CIdVJFk2s0cWZt1lOiMPMSXycSYJ45wMk7Lly3uoqRx0uWfFK5iT2OCv+fJi5jTng==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "@aws-sdk/util-buffer-from": "3.208.0", + "@aws-sdk/util-utf8": "3.254.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/invalid-dependency": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.272.0.tgz", + "integrity": "sha512-ysW6wbjl1Y78txHUQ/Tldj2Rg1BI7rpMO9B9xAF6yAX3mQ7t6SUPQG/ewOGvH2208NBIl3qP5e/hDf0Q6r/1iw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/is-array-buffer": { + "version": "3.201.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.201.0.tgz", + "integrity": "sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-content-length": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.272.0.tgz", + "integrity": "sha512-sAbDZSTNmLX+UTGwlUHJBWy0QGQkiClpHwVFXACon+aG0ySLNeRKEVYs6NCPYldw4cj6hveLUn50cX44ukHErw==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.272.0.tgz", + "integrity": "sha512-Dk3JVjj7SxxoUKv3xGiOeBksvPtFhTDrVW75XJ98Ymv8gJH5L1sq4hIeJAHRKogGiRFq2J73mnZSlM9FVXEylg==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-serde": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/signature-v4": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/url-parser": "3.272.0", + "@aws-sdk/util-config-provider": "3.208.0", + "@aws-sdk/util-middleware": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.272.0.tgz", + "integrity": "sha512-Q8K7bMMFZnioUXpxn57HIt4p+I63XaNAawMLIZ5B4F2piyukbQeM9q2XVKMGwqLvijHR8CyP5nHrtKqVuINogQ==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.272.0.tgz", + "integrity": "sha512-u2SQ0hWrFwxbxxYMG5uMEgf01pQY5jauK/LYWgGIvuCmFgiyRQQP3oN7kkmsxnS9MWmNmhbyQguX2NY02s5e9w==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.272.0.tgz", + "integrity": "sha512-Gp/eKWeUWVNiiBdmUM2qLkBv+VLSJKoWAO+aKmyxxwjjmWhE0FrfA1NQ1a3g+NGMhRbAfQdaYswRAKsul70ISg==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-retry": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.272.0.tgz", + "integrity": "sha512-pCGvHM7C76VbO/dFerH+Vwf7tGv7j+e+eGrvhQ35mRghCtfIou/WMfTZlD1TNee93crrAQQVZKjtW3dMB3WCzg==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/service-error-classification": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/util-middleware": "3.272.0", + "@aws-sdk/util-retry": "3.272.0", + "tslib": "^2.3.1", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.272.0.tgz", + "integrity": "sha512-VvYPg7LrDIjUOWueSzo2wBzcNG7dw+cmzV6zAKaLxf0RC5jeAP4hE0OzDiiZfDrjNghEzgq/V+0NO+LewqYL9Q==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/signature-v4": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-serde": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.272.0.tgz", + "integrity": "sha512-kW1uOxgPSwtXPB5rm3QLdWomu42lkYpQL94tM1BjyFOWmBLO2lQhk5a7Dw6HkTozT9a+vxtscLChRa6KZe61Hw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-signing": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.272.0.tgz", + "integrity": "sha512-4LChFK4VAR91X+dupqM8fQqYhFGE0G4Bf9rQlVTgGSbi2KUOmpqXzH0/WKE228nKuEhmH8+Qd2VPSAE2JcyAUA==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/signature-v4": "3.272.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/util-middleware": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-stack": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.272.0.tgz", + "integrity": "sha512-jhwhknnPBGhfXAGV5GXUWfEhDFoP/DN8MPCO2yC5OAxyp6oVJ8lTPLkZYMTW5VL0c0eG44dXpF4Ib01V+PlDrQ==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.272.0.tgz", + "integrity": "sha512-Qy7/0fsDJxY5l0bEk7WKDfqb4Os/sCAgFR2zEvrhDtbkhYPf72ysvg/nRUTncmCbo8tOok4SJii2myk8KMfjjw==", + "optional": true, + "dependencies": { + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-config-provider": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.272.0.tgz", + "integrity": "sha512-YYCIBh9g1EQo7hm2l22HX5Yr9RoPQ2RCvhzKvF1n1e8t1QH4iObQrYUtqHG4khcm64Cft8C5MwZmgzHbya5Z6Q==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/shared-ini-file-loader": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/node-http-handler": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.272.0.tgz", + "integrity": "sha512-VrW9PjhhngeyYp4yGYPe5S0vgZH6NwU3Po9xAgayUeE37Inr7LS1YteFMHdpgsUUeNXnh7d06CXqHo1XjtqOKA==", + "optional": true, + "dependencies": { + "@aws-sdk/abort-controller": "3.272.0", + "@aws-sdk/protocol-http": "3.272.0", + "@aws-sdk/querystring-builder": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/property-provider": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.272.0.tgz", + "integrity": "sha512-V1pZTaH5eqpAt8O8CzbItHhOtzIfFuWymvwZFkAtwKuaHpnl7jjrTouV482zoq8AD/fF+VVSshwBKYA7bhidIw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.272.0.tgz", + "integrity": "sha512-4JQ54v5Yn08jspNDeHo45CaSn1CvTJqS1Ywgr79eU6jBExtguOWv6LNtwVSBD9X37v88iqaxt8iu1Z3pZZAJeg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-builder": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.272.0.tgz", + "integrity": "sha512-ndo++7GkdCj5tBXE6rGcITpSpZS4PfyV38wntGYAlj9liL1omk3bLZRY6uzqqkJpVHqbg2fD7O2qHNItzZgqhw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "@aws-sdk/util-uri-escape": "3.201.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/querystring-parser": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.272.0.tgz", + "integrity": "sha512-5oS4/9n6N1LZW9tI3qq/0GnCuWoOXRgcHVB+AJLRBvDbEe+GI+C/xK1tKLsfpDNgsQJHc4IPQoIt4megyZ/1+A==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/service-error-classification": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.272.0.tgz", + "integrity": "sha512-REoltM1LK9byyIufLqx9znhSolPcHQgVHIA2S0zu5sdt5qER4OubkLAXuo4MBbisUTmh8VOOvIyUb5ijZCXq1w==", + "optional": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/shared-ini-file-loader": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.272.0.tgz", + "integrity": "sha512-lzFPohp5sy2XvwFjZIzLVCRpC0i5cwBiaXmFzXYQZJm6FSCszHO4ax+m9yrtlyVFF/2YPWl+/bzNthy4aJtseA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.272.0.tgz", + "integrity": "sha512-pWxnHG1NqJWMwlhJ6NHNiUikOL00DHROmxah6krJPMPq4I3am2KY2Rs/8ouWhnEXKaHAv4EQhSALJ+7Mq5S4/A==", + "optional": true, + "dependencies": { + "@aws-sdk/is-array-buffer": "3.201.0", + "@aws-sdk/types": "3.272.0", + "@aws-sdk/util-hex-encoding": "3.201.0", + "@aws-sdk/util-middleware": "3.272.0", + "@aws-sdk/util-uri-escape": "3.201.0", + "@aws-sdk/util-utf8": "3.254.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/smithy-client": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.272.0.tgz", + "integrity": "sha512-pvdleJ3kaRvyRw2pIZnqL85ZlWBOZrPKmR9I69GCvlyrfdjRBhbSjIEZ+sdhZudw0vdHxq25AGoLUXhofVLf5Q==", + "optional": true, + "dependencies": { + "@aws-sdk/middleware-stack": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.272.0.tgz", + "integrity": "sha512-0GISJ4IKN2rXvbSddB775VjBGSKhYIGQnAdMqbvxi9LB6pSvVxcH9aIL28G0spiuL+dy3yGQZ8RlJPAyP9JW9A==", + "optional": true, + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/shared-ini-file-loader": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.272.0.tgz", + "integrity": "sha512-MmmL6vxMGP5Bsi+4wRx4mxYlU/LX6M0noOXrDh/x5FfG7/4ZOar/nDxqDadhJtNM88cuWVHZWY59P54JzkGWmA==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/url-parser": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.272.0.tgz", + "integrity": "sha512-vX/Tx02PlnQ/Kgtf5TnrNDHPNbY+amLZjW0Z1d9vzAvSZhQ4i9Y18yxoRDIaDTCNVRDjdhV8iuctW+05PB5JtQ==", + "optional": true, + "dependencies": { + "@aws-sdk/querystring-parser": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-base64": { + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-base64/-/util-base64-3.208.0.tgz", + "integrity": "sha512-PQniZph5A6N7uuEOQi+1hnMz/FSOK/8kMFyFO+4DgA1dZ5pcKcn5wiFwHkcTb/BsgVqQa3Jx0VHNnvhlS8JyTg==", + "optional": true, + "dependencies": { + "@aws-sdk/util-buffer-from": "3.208.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-body-length-browser": { + "version": "3.188.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.188.0.tgz", + "integrity": "sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-body-length-node": { + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.208.0.tgz", + "integrity": "sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-buffer-from": { + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.208.0.tgz", + "integrity": "sha512-7L0XUixNEFcLUGPeBF35enCvB9Xl+K6SQsmbrPk1P3mlV9mguWSDQqbOBwY1Ir0OVbD6H/ZOQU7hI/9RtRI0Zw==", + "optional": true, + "dependencies": { + "@aws-sdk/is-array-buffer": "3.201.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-config-provider": { + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.208.0.tgz", + "integrity": "sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-browser": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.272.0.tgz", + "integrity": "sha512-W8ZVJSZRuUBg8l0JEZzUc+9fKlthVp/cdE+pFeF8ArhZelOLCiaeCrMaZAeJusaFzIpa6cmOYQAjtSMVyrwRtg==", + "optional": true, + "dependencies": { + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/types": "3.272.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-defaults-mode-node": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.272.0.tgz", + "integrity": "sha512-U0NTcbMw6KFk7uz/avBmfxQSTREEiX6JDMH68oN/3ux4AICd2I4jHyxnloSWGuiER1FxZf1dEJ8ZTwy8Ibl21Q==", + "optional": true, + "dependencies": { + "@aws-sdk/config-resolver": "3.272.0", + "@aws-sdk/credential-provider-imds": "3.272.0", + "@aws-sdk/node-config-provider": "3.272.0", + "@aws-sdk/property-provider": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.272.0.tgz", + "integrity": "sha512-c4MPUaJt2G6gGpoiwIOqDfUa98c1J63RpYvf/spQEKOtC/tF5Gfqlxuq8FnAl5lHnrqj1B9ZXLLxFhHtDR0IiQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-hex-encoding": { + "version": "3.201.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.201.0.tgz", + "integrity": "sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.208.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.208.0.tgz", + "integrity": "sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-middleware": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.272.0.tgz", + "integrity": "sha512-Abw8m30arbwxqmeMMha5J11ESpHUNmCeSqSzE8/C4B8jZQtHY4kq7f+upzcNIQ11lsd+uzBEzNG3+dDRi0XOJQ==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-retry": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-retry/-/util-retry-3.272.0.tgz", + "integrity": "sha512-Ngha5414LR4gRHURVKC9ZYXsEJhMkm+SJ+44wlzOhavglfdcKKPUsibz5cKY1jpUV7oKECwaxHWpBB8r6h+hOg==", + "optional": true, + "dependencies": { + "@aws-sdk/service-error-classification": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/util-uri-escape": { + "version": "3.201.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.201.0.tgz", + "integrity": "sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.272.0.tgz", + "integrity": "sha512-Lp5QX5bH6uuwBlIdr7w7OAcAI50ttyskb++yUr9i+SPvj6RI2dsfIBaK4mDg1qUdM5LeUdvIyqwj3XHjFKAAvA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.272.0", + "bowser": "^2.11.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.272.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.272.0.tgz", + "integrity": "sha512-ljK+R3l+Q1LIHrcR+Knhk0rmcSkfFadZ8V+crEGpABf/QUQRg7NkZMsoe814tfBO5F7tMxo8wwwSdaVNNHtoRA==", + "optional": true, + "dependencies": { + "@aws-sdk/node-config-provider": "3.272.0", + "@aws-sdk/types": "3.272.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8": { + "version": "3.254.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8/-/util-utf8-3.254.0.tgz", + "integrity": "sha512-14Kso/eIt5/qfIBmhEL9L1IfyUqswjSTqO2mY7KOzUZ9SZbwn3rpxmtkhmATkRjD7XIlLKaxBkI7tU9Zjzj8Kw==", + "optional": true, + "dependencies": { + "@aws-sdk/util-buffer-from": "3.208.0", + "tslib": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@sendgrid/client": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.7.0.tgz", + "integrity": "sha512-SxH+y8jeAQSnDavrTD0uGDXYIIkFylCo+eDofVmZLQ0f862nnqbC3Vd1ej6b7Le7lboyzQF6F7Fodv02rYspuA==", + "dependencies": { + "@sendgrid/helpers": "^7.7.0", + "axios": "^0.26.0" + }, + "engines": { + "node": "6.* || 8.* || >=10.*" + } + }, + "node_modules/@sendgrid/helpers": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-7.7.0.tgz", + "integrity": "sha512-3AsAxfN3GDBcXoZ/y1mzAAbKzTtUZ5+ZrHOmWQ279AuaFXUNCh9bPnRpN504bgveTqoW+11IzPg3I0WVgDINpw==", + "dependencies": { + "deepmerge": "^4.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@sendgrid/mail": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-7.7.0.tgz", + "integrity": "sha512-5+nApPE9wINBvHSUxwOxkkQqM/IAAaBYoP9hw7WwgDNQPxraruVqHizeTitVtKGiqWCKm2mnjh4XGN3fvFLqaw==", + "dependencies": { + "@sendgrid/client": "^7.7.0", + "@sendgrid/helpers": "^7.7.0" + }, + "engines": { + "node": "6.* || 8.* || >=10.*" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz", + "integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==" + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bcrypt": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", + "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.10", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/deepmerge": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/dotty": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotty/-/dotty-0.1.2.tgz", + "integrity": "sha512-V0EWmKeH3DEhMwAZ+8ZB2Ao4OK6p++Z0hsDtZq3N0+0ZMVqkzrcEGROvOnZpLnvBg5PTNG23JEDLAm64gPaotQ==" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-session": { + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "dependencies": { + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/fast-xml-parser": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz", + "integrity": "sha512-4aUg3aNRR/WjQAcpceODG1C3x3lFANXRo8+1biqfieHmg9pyMt7qB4lQV/Ta6sJCTbA5vfD8fnA8S54JATiFUA==", + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/generaterr": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/generaterr/-/generaterr-1.5.0.tgz", + "integrity": "sha512-JgcGRv2yUKeboLvvNrq9Bm90P4iJBu7/vd5wSLYqMG5GJ6SxZT46LAAkMfNhQ+EK3jzC+cRBm7P8aUWYyphgcQ==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/json-stable-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", + "dependencies": { + "jsonify": "^0.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "dependencies": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "dependencies": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/method-override/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.3.tgz", + "integrity": "sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mongodb": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.13.0.tgz", + "integrity": "sha512-+taZ/bV8d1pYuHL4U+gSwkhmDrwkWbH1l4aah4YpmpscMwgFBkufIKxgP/G7m87/NUuQzc2Z75ZTI7ZOyqZLbw==", + "dependencies": { + "bson": "^4.7.0", + "mongodb-connection-string-url": "^2.5.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "saslprep": "^1.0.3" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongoose": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.9.1.tgz", + "integrity": "sha512-hOz1ZWV0w6WEVLrj89Wpk7PXDYtDDF6k7/NX79lY5iKqeFtZsceBXW8xW59YFNcW5O3cH32hQ8IbDlhgyBsDMA==", + "dependencies": { + "bson": "^4.7.0", + "kareem": "2.5.1", + "mongodb": "4.13.0", + "mpath": "0.9.0", + "mquery": "4.0.3", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose-encryption": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mongoose-encryption/-/mongoose-encryption-2.1.2.tgz", + "integrity": "sha512-whc9ZhN8/UjED3qiQKJvQAx/H1Ml3FUpEiyoHLKl4gzhvWorndnRE0sKuQ3fKzuaZDi/rDBN5AI4L/J2Er8Q9Q==", + "dependencies": { + "async": "^2.6.1", + "buffer-equal-constant-time": "^1.0.1", + "dotty": "^0.1.2", + "json-stable-stringify": "^1.0.0", + "mpath": "^0.8.4", + "semver": "^5.5.0", + "underscore": "^1.5.0" + }, + "engines": { + "node": ">=4.4.5" + }, + "peerDependencies": { + "mongoose": ">=5.0.0" + } + }, + "node_modules/mongoose-encryption/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/mongoose-encryption/node_modules/mpath": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz", + "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mongoose-findorcreate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongoose-findorcreate/-/mongoose-findorcreate-3.0.0.tgz", + "integrity": "sha512-kQhDe5XDj6tMv8kq1wjK+hITGIGUl60rj8oGLupF9poNsqIDkAJBXudZKcCdSyBZ7p6DLK2+0jSBthrb26tSYQ==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/nodemailer": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.1.tgz", + "integrity": "sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nodemon": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==", + "dependencies": { + "passport-strategy": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-local-mongoose": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/passport-local-mongoose/-/passport-local-mongoose-7.1.2.tgz", + "integrity": "sha512-hNLIKi/6IhElr/PhOze8wLDh7T4+ZYhc8GFWYApLgG7FrjI55tuGZELPtsUBqODz77OwlUUf+ngPgHN09zxGLg==", + "dependencies": { + "generaterr": "^1.5.0", + "passport-local": "^1.0.0", + "scmp": "^2.1.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz", + "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/saslprep": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", + "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/scmp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", + "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^4.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "optional": true + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 000000000..4b7ad21c4 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,43 @@ +{ + "name": "allpurdue", + "version": "1.0.0", + "description": "CS 307 Project", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/kabrap/allpurdue.git" + }, + "author": "Team 10", + "license": "ISC", + "bugs": { + "url": "https://github.com/kabrap/allpurdue/issues" + }, + "homepage": "https://github.com/kabrap/allpurdue#readme", + "dependencies": { + "@sendgrid/mail": "^7.7.0", + "bcrypt": "^5.1.0", + "cors": "^2.8.5", + "dotenv": "^16.0.3", + "ejs": "^3.1.8", + "express": "^4.18.2", + "express-session": "^1.17.3", + "fs": "^0.0.1-security", + "jsonwebtoken": "^9.0.0", + "md5": "^2.3.0", + "method-override": "^3.0.0", + "mongoose": "^6.9.1", + "mongoose-encryption": "^2.1.2", + "mongoose-findorcreate": "^3.0.0", + "multer": "^1.4.5-lts.1", + "nodemailer": "^6.9.1", + "nodemon": "^2.0.20", + "passport": "^0.6.0", + "passport-google-oauth20": "^2.0.0", + "passport-local": "^1.0.0", + "passport-local-mongoose": "^7.1.2", + "socket.io": "^4.6.1" + } +} diff --git a/backend/public/css/bootstrap-social.css b/backend/public/css/bootstrap-social.css new file mode 100644 index 000000000..93e16f766 --- /dev/null +++ b/backend/public/css/bootstrap-social.css @@ -0,0 +1,147 @@ +/* + * Social Buttons for Bootstrap + * + * Copyright 2013-2016 Panayiotis Lipiridis + * Licensed under the MIT License + * + * https://github.com/lipis/bootstrap-social + */ + +.btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} +.btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em} +.btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em} +.btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em} +.btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} +.btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em} +.btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em} +.btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em} +.btn-social-icon>:first-child{border:none;text-align:center;width:100% !important} +.btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0} +.btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0} +.btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0} +.btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:focus,.btn-adn.focus{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)} +.btn-adn:hover{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)} +.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)}.btn-adn:active:hover,.btn-adn.active:hover,.open>.dropdown-toggle.btn-adn:hover,.btn-adn:active:focus,.btn-adn.active:focus,.open>.dropdown-toggle.btn-adn:focus,.btn-adn:active.focus,.btn-adn.active.focus,.open>.dropdown-toggle.btn-adn.focus{color:#fff;background-color:#b94630;border-color:rgba(0,0,0,0.2)} +.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none} +.btn-adn.disabled:hover,.btn-adn[disabled]:hover,fieldset[disabled] .btn-adn:hover,.btn-adn.disabled:focus,.btn-adn[disabled]:focus,fieldset[disabled] .btn-adn:focus,.btn-adn.disabled.focus,.btn-adn[disabled].focus,fieldset[disabled] .btn-adn.focus{background-color:#d87a68;border-color:rgba(0,0,0,0.2)} +.btn-adn .badge{color:#d87a68;background-color:#fff} +.btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:focus,.btn-bitbucket.focus{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)} +.btn-bitbucket:hover{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)} +.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:active:hover,.btn-bitbucket.active:hover,.open>.dropdown-toggle.btn-bitbucket:hover,.btn-bitbucket:active:focus,.btn-bitbucket.active:focus,.open>.dropdown-toggle.btn-bitbucket:focus,.btn-bitbucket:active.focus,.btn-bitbucket.active.focus,.open>.dropdown-toggle.btn-bitbucket.focus{color:#fff;background-color:#0f253c;border-color:rgba(0,0,0,0.2)} +.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none} +.btn-bitbucket.disabled:hover,.btn-bitbucket[disabled]:hover,fieldset[disabled] .btn-bitbucket:hover,.btn-bitbucket.disabled:focus,.btn-bitbucket[disabled]:focus,fieldset[disabled] .btn-bitbucket:focus,.btn-bitbucket.disabled.focus,.btn-bitbucket[disabled].focus,fieldset[disabled] .btn-bitbucket.focus{background-color:#205081;border-color:rgba(0,0,0,0.2)} +.btn-bitbucket .badge{color:#205081;background-color:#fff} +.btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:focus,.btn-dropbox.focus{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)} +.btn-dropbox:hover{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)} +.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)}.btn-dropbox:active:hover,.btn-dropbox.active:hover,.open>.dropdown-toggle.btn-dropbox:hover,.btn-dropbox:active:focus,.btn-dropbox.active:focus,.open>.dropdown-toggle.btn-dropbox:focus,.btn-dropbox:active.focus,.btn-dropbox.active.focus,.open>.dropdown-toggle.btn-dropbox.focus{color:#fff;background-color:#0a568c;border-color:rgba(0,0,0,0.2)} +.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none} +.btn-dropbox.disabled:hover,.btn-dropbox[disabled]:hover,fieldset[disabled] .btn-dropbox:hover,.btn-dropbox.disabled:focus,.btn-dropbox[disabled]:focus,fieldset[disabled] .btn-dropbox:focus,.btn-dropbox.disabled.focus,.btn-dropbox[disabled].focus,fieldset[disabled] .btn-dropbox.focus{background-color:#1087dd;border-color:rgba(0,0,0,0.2)} +.btn-dropbox .badge{color:#1087dd;background-color:#fff} +.btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:focus,.btn-facebook.focus{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)} +.btn-facebook:hover{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)} +.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)}.btn-facebook:active:hover,.btn-facebook.active:hover,.open>.dropdown-toggle.btn-facebook:hover,.btn-facebook:active:focus,.btn-facebook.active:focus,.open>.dropdown-toggle.btn-facebook:focus,.btn-facebook:active.focus,.btn-facebook.active.focus,.open>.dropdown-toggle.btn-facebook.focus{color:#fff;background-color:#23345a;border-color:rgba(0,0,0,0.2)} +.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none} +.btn-facebook.disabled:hover,.btn-facebook[disabled]:hover,fieldset[disabled] .btn-facebook:hover,.btn-facebook.disabled:focus,.btn-facebook[disabled]:focus,fieldset[disabled] .btn-facebook:focus,.btn-facebook.disabled.focus,.btn-facebook[disabled].focus,fieldset[disabled] .btn-facebook.focus{background-color:#3b5998;border-color:rgba(0,0,0,0.2)} +.btn-facebook .badge{color:#3b5998;background-color:#fff} +.btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:focus,.btn-flickr.focus{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)} +.btn-flickr:hover{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)} +.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)}.btn-flickr:active:hover,.btn-flickr.active:hover,.open>.dropdown-toggle.btn-flickr:hover,.btn-flickr:active:focus,.btn-flickr.active:focus,.open>.dropdown-toggle.btn-flickr:focus,.btn-flickr:active.focus,.btn-flickr.active.focus,.open>.dropdown-toggle.btn-flickr.focus{color:#fff;background-color:#a80057;border-color:rgba(0,0,0,0.2)} +.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none} +.btn-flickr.disabled:hover,.btn-flickr[disabled]:hover,fieldset[disabled] .btn-flickr:hover,.btn-flickr.disabled:focus,.btn-flickr[disabled]:focus,fieldset[disabled] .btn-flickr:focus,.btn-flickr.disabled.focus,.btn-flickr[disabled].focus,fieldset[disabled] .btn-flickr.focus{background-color:#ff0084;border-color:rgba(0,0,0,0.2)} +.btn-flickr .badge{color:#ff0084;background-color:#fff} +.btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:focus,.btn-foursquare.focus{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)} +.btn-foursquare:hover{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)} +.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)}.btn-foursquare:active:hover,.btn-foursquare.active:hover,.open>.dropdown-toggle.btn-foursquare:hover,.btn-foursquare:active:focus,.btn-foursquare.active:focus,.open>.dropdown-toggle.btn-foursquare:focus,.btn-foursquare:active.focus,.btn-foursquare.active.focus,.open>.dropdown-toggle.btn-foursquare.focus{color:#fff;background-color:#e30742;border-color:rgba(0,0,0,0.2)} +.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none} +.btn-foursquare.disabled:hover,.btn-foursquare[disabled]:hover,fieldset[disabled] .btn-foursquare:hover,.btn-foursquare.disabled:focus,.btn-foursquare[disabled]:focus,fieldset[disabled] .btn-foursquare:focus,.btn-foursquare.disabled.focus,.btn-foursquare[disabled].focus,fieldset[disabled] .btn-foursquare.focus{background-color:#f94877;border-color:rgba(0,0,0,0.2)} +.btn-foursquare .badge{color:#f94877;background-color:#fff} +.btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:focus,.btn-github.focus{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)} +.btn-github:hover{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)} +.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)}.btn-github:active:hover,.btn-github.active:hover,.open>.dropdown-toggle.btn-github:hover,.btn-github:active:focus,.btn-github.active:focus,.open>.dropdown-toggle.btn-github:focus,.btn-github:active.focus,.btn-github.active.focus,.open>.dropdown-toggle.btn-github.focus{color:#fff;background-color:#191919;border-color:rgba(0,0,0,0.2)} +.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none} +.btn-github.disabled:hover,.btn-github[disabled]:hover,fieldset[disabled] .btn-github:hover,.btn-github.disabled:focus,.btn-github[disabled]:focus,fieldset[disabled] .btn-github:focus,.btn-github.disabled.focus,.btn-github[disabled].focus,fieldset[disabled] .btn-github.focus{background-color:#444;border-color:rgba(0,0,0,0.2)} +.btn-github .badge{color:#444;background-color:#fff} +.btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:focus,.btn-google.focus{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)} +.btn-google:hover{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)} +.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active:hover,.btn-google.active:hover,.open>.dropdown-toggle.btn-google:hover,.btn-google:active:focus,.btn-google.active:focus,.open>.dropdown-toggle.btn-google:focus,.btn-google:active.focus,.btn-google.active.focus,.open>.dropdown-toggle.btn-google.focus{color:#fff;background-color:#a32b1c;border-color:rgba(0,0,0,0.2)} +.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none} +.btn-google.disabled:hover,.btn-google[disabled]:hover,fieldset[disabled] .btn-google:hover,.btn-google.disabled:focus,.btn-google[disabled]:focus,fieldset[disabled] .btn-google:focus,.btn-google.disabled.focus,.btn-google[disabled].focus,fieldset[disabled] .btn-google.focus{background-color:#dd4b39;border-color:rgba(0,0,0,0.2)} +.btn-google .badge{color:#dd4b39;background-color:#fff} +.btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:focus,.btn-instagram.focus{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)} +.btn-instagram:hover{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)} +.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)}.btn-instagram:active:hover,.btn-instagram.active:hover,.open>.dropdown-toggle.btn-instagram:hover,.btn-instagram:active:focus,.btn-instagram.active:focus,.open>.dropdown-toggle.btn-instagram:focus,.btn-instagram:active.focus,.btn-instagram.active.focus,.open>.dropdown-toggle.btn-instagram.focus{color:#fff;background-color:#26455d;border-color:rgba(0,0,0,0.2)} +.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none} +.btn-instagram.disabled:hover,.btn-instagram[disabled]:hover,fieldset[disabled] .btn-instagram:hover,.btn-instagram.disabled:focus,.btn-instagram[disabled]:focus,fieldset[disabled] .btn-instagram:focus,.btn-instagram.disabled.focus,.btn-instagram[disabled].focus,fieldset[disabled] .btn-instagram.focus{background-color:#3f729b;border-color:rgba(0,0,0,0.2)} +.btn-instagram .badge{color:#3f729b;background-color:#fff} +.btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:focus,.btn-linkedin.focus{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)} +.btn-linkedin:hover{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)} +.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)}.btn-linkedin:active:hover,.btn-linkedin.active:hover,.open>.dropdown-toggle.btn-linkedin:hover,.btn-linkedin:active:focus,.btn-linkedin.active:focus,.open>.dropdown-toggle.btn-linkedin:focus,.btn-linkedin:active.focus,.btn-linkedin.active.focus,.open>.dropdown-toggle.btn-linkedin.focus{color:#fff;background-color:#00405f;border-color:rgba(0,0,0,0.2)} +.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none} +.btn-linkedin.disabled:hover,.btn-linkedin[disabled]:hover,fieldset[disabled] .btn-linkedin:hover,.btn-linkedin.disabled:focus,.btn-linkedin[disabled]:focus,fieldset[disabled] .btn-linkedin:focus,.btn-linkedin.disabled.focus,.btn-linkedin[disabled].focus,fieldset[disabled] .btn-linkedin.focus{background-color:#007bb6;border-color:rgba(0,0,0,0.2)} +.btn-linkedin .badge{color:#007bb6;background-color:#fff} +.btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:focus,.btn-microsoft.focus{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)} +.btn-microsoft:hover{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)} +.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)}.btn-microsoft:active:hover,.btn-microsoft.active:hover,.open>.dropdown-toggle.btn-microsoft:hover,.btn-microsoft:active:focus,.btn-microsoft.active:focus,.open>.dropdown-toggle.btn-microsoft:focus,.btn-microsoft:active.focus,.btn-microsoft.active.focus,.open>.dropdown-toggle.btn-microsoft.focus{color:#fff;background-color:#0f4bac;border-color:rgba(0,0,0,0.2)} +.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none} +.btn-microsoft.disabled:hover,.btn-microsoft[disabled]:hover,fieldset[disabled] .btn-microsoft:hover,.btn-microsoft.disabled:focus,.btn-microsoft[disabled]:focus,fieldset[disabled] .btn-microsoft:focus,.btn-microsoft.disabled.focus,.btn-microsoft[disabled].focus,fieldset[disabled] .btn-microsoft.focus{background-color:#2672ec;border-color:rgba(0,0,0,0.2)} +.btn-microsoft .badge{color:#2672ec;background-color:#fff} +.btn-odnoklassniki{color:#fff;background-color:#f4731c;border-color:rgba(0,0,0,0.2)}.btn-odnoklassniki:focus,.btn-odnoklassniki.focus{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)} +.btn-odnoklassniki:hover{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)} +.btn-odnoklassniki:active,.btn-odnoklassniki.active,.open>.dropdown-toggle.btn-odnoklassniki{color:#fff;background-color:#d35b0a;border-color:rgba(0,0,0,0.2)}.btn-odnoklassniki:active:hover,.btn-odnoklassniki.active:hover,.open>.dropdown-toggle.btn-odnoklassniki:hover,.btn-odnoklassniki:active:focus,.btn-odnoklassniki.active:focus,.open>.dropdown-toggle.btn-odnoklassniki:focus,.btn-odnoklassniki:active.focus,.btn-odnoklassniki.active.focus,.open>.dropdown-toggle.btn-odnoklassniki.focus{color:#fff;background-color:#b14c09;border-color:rgba(0,0,0,0.2)} +.btn-odnoklassniki:active,.btn-odnoklassniki.active,.open>.dropdown-toggle.btn-odnoklassniki{background-image:none} +.btn-odnoklassniki.disabled:hover,.btn-odnoklassniki[disabled]:hover,fieldset[disabled] .btn-odnoklassniki:hover,.btn-odnoklassniki.disabled:focus,.btn-odnoklassniki[disabled]:focus,fieldset[disabled] .btn-odnoklassniki:focus,.btn-odnoklassniki.disabled.focus,.btn-odnoklassniki[disabled].focus,fieldset[disabled] .btn-odnoklassniki.focus{background-color:#f4731c;border-color:rgba(0,0,0,0.2)} +.btn-odnoklassniki .badge{color:#f4731c;background-color:#fff} +.btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:focus,.btn-openid.focus{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)} +.btn-openid:hover{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)} +.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active:hover,.btn-openid.active:hover,.open>.dropdown-toggle.btn-openid:hover,.btn-openid:active:focus,.btn-openid.active:focus,.open>.dropdown-toggle.btn-openid:focus,.btn-openid:active.focus,.btn-openid.active.focus,.open>.dropdown-toggle.btn-openid.focus{color:#fff;background-color:#b86607;border-color:rgba(0,0,0,0.2)} +.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none} +.btn-openid.disabled:hover,.btn-openid[disabled]:hover,fieldset[disabled] .btn-openid:hover,.btn-openid.disabled:focus,.btn-openid[disabled]:focus,fieldset[disabled] .btn-openid:focus,.btn-openid.disabled.focus,.btn-openid[disabled].focus,fieldset[disabled] .btn-openid.focus{background-color:#f7931e;border-color:rgba(0,0,0,0.2)} +.btn-openid .badge{color:#f7931e;background-color:#fff} +.btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:focus,.btn-pinterest.focus{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)} +.btn-pinterest:hover{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)} +.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)}.btn-pinterest:active:hover,.btn-pinterest.active:hover,.open>.dropdown-toggle.btn-pinterest:hover,.btn-pinterest:active:focus,.btn-pinterest.active:focus,.open>.dropdown-toggle.btn-pinterest:focus,.btn-pinterest:active.focus,.btn-pinterest.active.focus,.open>.dropdown-toggle.btn-pinterest.focus{color:#fff;background-color:#801419;border-color:rgba(0,0,0,0.2)} +.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none} +.btn-pinterest.disabled:hover,.btn-pinterest[disabled]:hover,fieldset[disabled] .btn-pinterest:hover,.btn-pinterest.disabled:focus,.btn-pinterest[disabled]:focus,fieldset[disabled] .btn-pinterest:focus,.btn-pinterest.disabled.focus,.btn-pinterest[disabled].focus,fieldset[disabled] .btn-pinterest.focus{background-color:#cb2027;border-color:rgba(0,0,0,0.2)} +.btn-pinterest .badge{color:#cb2027;background-color:#fff} +.btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:focus,.btn-reddit.focus{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)} +.btn-reddit:hover{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)} +.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)}.btn-reddit:active:hover,.btn-reddit.active:hover,.open>.dropdown-toggle.btn-reddit:hover,.btn-reddit:active:focus,.btn-reddit.active:focus,.open>.dropdown-toggle.btn-reddit:focus,.btn-reddit:active.focus,.btn-reddit.active.focus,.open>.dropdown-toggle.btn-reddit.focus{color:#000;background-color:#98ccff;border-color:rgba(0,0,0,0.2)} +.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none} +.btn-reddit.disabled:hover,.btn-reddit[disabled]:hover,fieldset[disabled] .btn-reddit:hover,.btn-reddit.disabled:focus,.btn-reddit[disabled]:focus,fieldset[disabled] .btn-reddit:focus,.btn-reddit.disabled.focus,.btn-reddit[disabled].focus,fieldset[disabled] .btn-reddit.focus{background-color:#eff7ff;border-color:rgba(0,0,0,0.2)} +.btn-reddit .badge{color:#eff7ff;background-color:#000} +.btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:focus,.btn-soundcloud.focus{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)} +.btn-soundcloud:hover{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)} +.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:active:hover,.btn-soundcloud.active:hover,.open>.dropdown-toggle.btn-soundcloud:hover,.btn-soundcloud:active:focus,.btn-soundcloud.active:focus,.open>.dropdown-toggle.btn-soundcloud:focus,.btn-soundcloud:active.focus,.btn-soundcloud.active.focus,.open>.dropdown-toggle.btn-soundcloud.focus{color:#fff;background-color:#a83800;border-color:rgba(0,0,0,0.2)} +.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none} +.btn-soundcloud.disabled:hover,.btn-soundcloud[disabled]:hover,fieldset[disabled] .btn-soundcloud:hover,.btn-soundcloud.disabled:focus,.btn-soundcloud[disabled]:focus,fieldset[disabled] .btn-soundcloud:focus,.btn-soundcloud.disabled.focus,.btn-soundcloud[disabled].focus,fieldset[disabled] .btn-soundcloud.focus{background-color:#f50;border-color:rgba(0,0,0,0.2)} +.btn-soundcloud .badge{color:#f50;background-color:#fff} +.btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:focus,.btn-tumblr.focus{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)} +.btn-tumblr:hover{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)} +.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)}.btn-tumblr:active:hover,.btn-tumblr.active:hover,.open>.dropdown-toggle.btn-tumblr:hover,.btn-tumblr:active:focus,.btn-tumblr.active:focus,.open>.dropdown-toggle.btn-tumblr:focus,.btn-tumblr:active.focus,.btn-tumblr.active.focus,.open>.dropdown-toggle.btn-tumblr.focus{color:#fff;background-color:#111c26;border-color:rgba(0,0,0,0.2)} +.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none} +.btn-tumblr.disabled:hover,.btn-tumblr[disabled]:hover,fieldset[disabled] .btn-tumblr:hover,.btn-tumblr.disabled:focus,.btn-tumblr[disabled]:focus,fieldset[disabled] .btn-tumblr:focus,.btn-tumblr.disabled.focus,.btn-tumblr[disabled].focus,fieldset[disabled] .btn-tumblr.focus{background-color:#2c4762;border-color:rgba(0,0,0,0.2)} +.btn-tumblr .badge{color:#2c4762;background-color:#fff} +.btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:focus,.btn-twitter.focus{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)} +.btn-twitter:hover{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)} +.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)}.btn-twitter:active:hover,.btn-twitter.active:hover,.open>.dropdown-toggle.btn-twitter:hover,.btn-twitter:active:focus,.btn-twitter.active:focus,.open>.dropdown-toggle.btn-twitter:focus,.btn-twitter:active.focus,.btn-twitter.active.focus,.open>.dropdown-toggle.btn-twitter.focus{color:#fff;background-color:#1583d7;border-color:rgba(0,0,0,0.2)} +.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none} +.btn-twitter.disabled:hover,.btn-twitter[disabled]:hover,fieldset[disabled] .btn-twitter:hover,.btn-twitter.disabled:focus,.btn-twitter[disabled]:focus,fieldset[disabled] .btn-twitter:focus,.btn-twitter.disabled.focus,.btn-twitter[disabled].focus,fieldset[disabled] .btn-twitter.focus{background-color:#55acee;border-color:rgba(0,0,0,0.2)} +.btn-twitter .badge{color:#55acee;background-color:#fff} +.btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:focus,.btn-vimeo.focus{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)} +.btn-vimeo:hover{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)} +.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)}.btn-vimeo:active:hover,.btn-vimeo.active:hover,.open>.dropdown-toggle.btn-vimeo:hover,.btn-vimeo:active:focus,.btn-vimeo.active:focus,.open>.dropdown-toggle.btn-vimeo:focus,.btn-vimeo:active.focus,.btn-vimeo.active.focus,.open>.dropdown-toggle.btn-vimeo.focus{color:#fff;background-color:#0f7b9f;border-color:rgba(0,0,0,0.2)} +.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none} +.btn-vimeo.disabled:hover,.btn-vimeo[disabled]:hover,fieldset[disabled] .btn-vimeo:hover,.btn-vimeo.disabled:focus,.btn-vimeo[disabled]:focus,fieldset[disabled] .btn-vimeo:focus,.btn-vimeo.disabled.focus,.btn-vimeo[disabled].focus,fieldset[disabled] .btn-vimeo.focus{background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)} +.btn-vimeo .badge{color:#1ab7ea;background-color:#fff} +.btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:focus,.btn-vk.focus{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)} +.btn-vk:hover{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)} +.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)}.btn-vk:active:hover,.btn-vk.active:hover,.open>.dropdown-toggle.btn-vk:hover,.btn-vk:active:focus,.btn-vk.active:focus,.open>.dropdown-toggle.btn-vk:focus,.btn-vk:active.focus,.btn-vk.active.focus,.open>.dropdown-toggle.btn-vk.focus{color:#fff;background-color:#3a526b;border-color:rgba(0,0,0,0.2)} +.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none} +.btn-vk.disabled:hover,.btn-vk[disabled]:hover,fieldset[disabled] .btn-vk:hover,.btn-vk.disabled:focus,.btn-vk[disabled]:focus,fieldset[disabled] .btn-vk:focus,.btn-vk.disabled.focus,.btn-vk[disabled].focus,fieldset[disabled] .btn-vk.focus{background-color:#587ea3;border-color:rgba(0,0,0,0.2)} +.btn-vk .badge{color:#587ea3;background-color:#fff} +.btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:focus,.btn-yahoo.focus{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)} +.btn-yahoo:hover{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)} +.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)}.btn-yahoo:active:hover,.btn-yahoo.active:hover,.open>.dropdown-toggle.btn-yahoo:hover,.btn-yahoo:active:focus,.btn-yahoo.active:focus,.open>.dropdown-toggle.btn-yahoo:focus,.btn-yahoo:active.focus,.btn-yahoo.active.focus,.open>.dropdown-toggle.btn-yahoo.focus{color:#fff;background-color:#39074e;border-color:rgba(0,0,0,0.2)} +.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none} +.btn-yahoo.disabled:hover,.btn-yahoo[disabled]:hover,fieldset[disabled] .btn-yahoo:hover,.btn-yahoo.disabled:focus,.btn-yahoo[disabled]:focus,fieldset[disabled] .btn-yahoo:focus,.btn-yahoo.disabled.focus,.btn-yahoo[disabled].focus,fieldset[disabled] .btn-yahoo.focus{background-color:#720e9e;border-color:rgba(0,0,0,0.2)} +.btn-yahoo .badge{color:#720e9e;background-color:#fff} diff --git a/backend/public/css/styles.css b/backend/public/css/styles.css new file mode 100644 index 000000000..a5e3a444d --- /dev/null +++ b/backend/public/css/styles.css @@ -0,0 +1,16 @@ + +body { + background-color: #E8ECEF; +} + +.centered { + padding-top: 200px; + text-align: center; +} + +.secret-text { + text-align: center; + font-size: 2rem; + color: #fff; + background-color: #000; +} diff --git a/backend/uploads/a.jpg b/backend/uploads/a.jpg new file mode 100644 index 000000000..f8c20abdc Binary files /dev/null and b/backend/uploads/a.jpg differ diff --git a/backend/uploads/azukifull.png b/backend/uploads/azukifull.png new file mode 100644 index 000000000..4270d724f Binary files /dev/null and b/backend/uploads/azukifull.png differ diff --git a/backend/uploads/b.jpg b/backend/uploads/b.jpg new file mode 100644 index 000000000..b0c5d434b Binary files /dev/null and b/backend/uploads/b.jpg differ diff --git a/backend/uploads/basilthai.jpg b/backend/uploads/basilthai.jpg new file mode 100644 index 000000000..44c7315a0 Binary files /dev/null and b/backend/uploads/basilthai.jpg differ diff --git a/backend/uploads/c.jpg b/backend/uploads/c.jpg new file mode 100644 index 000000000..cd7c41f02 Binary files /dev/null and b/backend/uploads/c.jpg differ diff --git a/backend/uploads/greyhouse.jpg b/backend/uploads/greyhouse.jpg new file mode 100644 index 000000000..a0a070d19 Binary files /dev/null and b/backend/uploads/greyhouse.jpg differ diff --git a/backend/uploads/hicks.JPG b/backend/uploads/hicks.JPG new file mode 100644 index 000000000..59492b220 Binary files /dev/null and b/backend/uploads/hicks.JPG differ diff --git a/backend/uploads/hicks2.jpg b/backend/uploads/hicks2.jpg new file mode 100644 index 000000000..b1b43cd7e Binary files /dev/null and b/backend/uploads/hicks2.jpg differ diff --git a/backend/uploads/maru.jpg b/backend/uploads/maru.jpg new file mode 100644 index 000000000..c41f70654 Binary files /dev/null and b/backend/uploads/maru.jpg differ diff --git a/backend/views/add_place.ejs b/backend/views/add_place.ejs new file mode 100644 index 000000000..72bbf118d --- /dev/null +++ b/backend/views/add_place.ejs @@ -0,0 +1,62 @@ + + +
+ + +<%= place.description %>
+Average Rating: <%= place.avgRating %>
+Number of Reviews: <%= place.numReviews %>
+Type: <%= place.placeType %>
+ <% if (place.tags.length > 0) { %> +Tags: <%= place.tags.join(', ') %>
+ <% } %> +Location: <%= place.name %> on Google Maps
+ +<%= blog.text %>
+Author: <%= blog.author.name %>
+Likes: <%= blog.likes %>
+ + + All Blogs + + diff --git a/backend/views/blogs.ejs b/backend/views/blogs.ejs new file mode 100644 index 000000000..9c01ab796 --- /dev/null +++ b/backend/views/blogs.ejs @@ -0,0 +1,26 @@ + + + + + +<%= blog.text %>
+Author: <%= blog.author.name %>
+Likes: <%= blog.likes %>
+ View + +