diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..9d8594872 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +IMGUR_CLIENT_ID= +DEFAULT_AVATAR= +DEFAULT_BACKGROUND= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4daff2366..4dbac7a5d 100644 --- a/.gitignore +++ b/.gitignore @@ -84,4 +84,7 @@ typings/ .fusebox/ # DynamoDB Local files -.dynamodb/ \ No newline at end of file +.dynamodb/ + +test.js +default-img.json \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..6feca7ece --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: NODE_ENV=production node app.js \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..1a64a0cb6 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# Simple Twitter +使用 Node.js 搭配後端框架 Express 打造一個社群網站,可進行貼文﹑回覆其他使用者貼文﹑喜歡貼文﹑追蹤使用者…等功能。 +# Features:功能 +- 註冊/登入/登出 + - 使用者要登入才能使用網站 + - 使用者註冊重複/登入/登出失敗時,會看到對應的系統訊 +- 使用者 + - 使用者能在首頁瀏覽所有的推文 + - 使用者點擊貼文方塊時,能查看該則貼文的詳情與回覆串 + - 點擊貼文中使用者頭像時,能瀏覽該使用者的個人資料及推文 + - 使用者能回覆別人的推文 + - 使用者可以追蹤/取消追蹤其他使用者 + - 使用者能編輯自己的名稱、介紹、大頭照和個人背景 + - 使用者能在首頁的側邊欄,看見跟隨者數量排列前 8 的使用者推薦名單 +- 後台管理 + - 管理者可以瀏覽站內所有的使用者清單 + - 管理者可以瀏覽全站的推文清單 & 刪除 + + # Environment Setup:環境安裝 +- Node.js:v14.16.0 +- Express.js:4.16.4 +- Express-handlebars:^3.0.0 + +# Installing Procedure:安裝流程 +1.開啟終端機將專案存至本機: +``` +git clone https://github.com/EasonLu0425/twitter-fullstack-2020.git +``` +2.進入存放此專案的資料夾 +``` +3.環境變數設定 +將根目錄.env.example檔案中輸入imgur金鑰﹑預設頭貼網路位置﹑預設背景網路位置,再把.env.example檔案名稱修改為.env +4.建立資料庫 +開啟 MySQL workbench,再連線至本地資料庫,輸入以下建立資料庫 + +``` +drop database if exists ac_twitter_workspace; +create database ac_twitter_workspace; +use ac_twitter_workspace; + +drop database if exists ac_twitter_workspace_test; +create database ac_twitter_workspace_test; +use ac_twitter_workspace_test; +``` +5.安裝 npm 套件 +``` +npm install +``` +6.db:migrate 設定 +``` +npx sequelize db:migrate +``` +7.加入種子資料 +``` +npx sequelize db:seed:all +``` +8.啟動專案 +``` +npm run dev +``` +9.使用 +終端機出現下列訊息" "Example app listening on port 3000!" +可開啟瀏覽器輸入 http://localhost:3000 使用 + +10.預設使用者 Seed User +- 一般使用者帳號有20組 (帳號:user1﹑user2﹑user3…etc,密碼皆為12345678) +- 管理者帳號僅有1組 (帳號:root ,密碼:12345678) \ No newline at end of file diff --git a/app.js b/app.js index 80ee0bbf8..75dfd31ea 100644 --- a/app.js +++ b/app.js @@ -1,13 +1,68 @@ -const express = require('express') -const helpers = require('./_helpers'); - -const app = express() -const port = 3000 +if (process.env.NODE_ENV !== "production") { + require("dotenv").config(); +} +const express = require("express"); +const handlebarsHelpers = require("./helpers/handlebars-helpers"); +const handlebars = require("express-handlebars"); +const flash = require("connect-flash"); +const session = require("express-session"); +const passport = require("./config/passport"); +const routes = require("./routes"); +const helpers = require("./_helpers"); +const path = require("path"); +const methodOverride = require("method-override"); +const app = express(); +const port = process.env.PORT || 3000; +const SESSION_SECRET = "secret"; // use helpers.getUser(req) to replace req.user // use helpers.ensureAuthenticated(req) to replace req.isAuthenticated() -app.get('/', (req, res) => res.send('Hello World!')) -app.listen(port, () => console.log(`Example app listening on port ${port}!`)) +app.set("view engine", "hbs"); +app.engine("hbs", handlebars({ extname: ".hbs", helpers: handlebarsHelpers })); +app.use(express.urlencoded({ extended: true })); +app.use( + session({ secret: SESSION_SECRET, resave: false, saveUninitialized: false }) +); +app.use(passport.initialize()); +app.use(passport.session()); +app.use(methodOverride("_method")); +app.use(flash()); +app.use("/upload", express.static(path.join(__dirname, "upload"))); //上傳圖片 +app.use("/", express.static("public")); +app.use((req, res, next) => { + const { user } = req; + const { + success_messages, + error_messages, + warning_messages, + info_messages, + account_messages, + } = req.flash(); + + res.locals = { + currentUser: user, + success_messages, + error_messages, + warning_messages, + info_messages, + account_messages, + user: helpers.getUser(req), + paramsUser: req.params.user, + }; + + // res.locals.currentUser = req.user; + // res.locals.success_messages = req.flash("success_messages"); + // res.locals.error_messages = req.flash("error_messages"); + // res.locals.warning_messages = req.flash("warning_messages"); + // res.locals.info_messages = req.flash("info_messages"); + // res.locals.account_messages = req.flash("account_messages"); + res.locals.user = helpers.getUser(req); + res.locals.paramsUser = req.params.user; + next(); +}); + +app.use(routes); +app.listen(port, () => console.log(`Example app listening on port ${port}!`)); -module.exports = app +module.exports = app; diff --git a/config/config.json b/config/config.json index 64b9997e4..f22ab7a8e 100644 --- a/config/config.json +++ b/config/config.json @@ -15,11 +15,7 @@ "logging": false }, "production": { - "username": "root", - "password": null, - "database": "database_production", - "host": "127.0.0.1", - "dialect": "mysql" + "use_env_variable": "MYSQL_DATABASE_URL" }, "travis": { "username": "travis", diff --git a/config/passport.js b/config/passport.js new file mode 100644 index 000000000..21eb9cfb4 --- /dev/null +++ b/config/passport.js @@ -0,0 +1,35 @@ +const passport = require('passport') +const LocalStrategy = require('passport-local') +const bcrypt = require('bcryptjs') + +const { User } = require('../models') + +passport.use(new LocalStrategy( + { + usernameField: 'account', + passwordField: 'password', + passReqToCallback: true + }, + (req, account, password, cb) => { + User.findOne({ where: { account } }) + .then(user => { + if (!user) return cb(null, false, req.flash('account_messages', '帳號不存在!')) + bcrypt.compare(password, user.password).then(res => { + if (!res) return cb(null, false, req.flash('account_messages', '帳號或密碼輸入錯誤!')) + return cb(null, user) + }) + }) + } +)) + +passport.serializeUser((user, cb) => { + cb(null, user.id) +}) + +passport.deserializeUser((id, cb) => { + User.findByPk(id) + .then(user => cb(null, user.toJSON())) + .catch(err => cb(err)) +}) + +module.exports = passport diff --git a/controllers/admin-controller.js b/controllers/admin-controller.js new file mode 100644 index 000000000..00eba362b --- /dev/null +++ b/controllers/admin-controller.js @@ -0,0 +1,75 @@ +const { sequelize, Tweet, User, Like, Reply } = require('../models') + +const adminController = { + signinPage: (req, res) => { + res.render('admin/signin') + }, + signin: (req, res) => { + if (req.user.role === 'user') { + req.flash('account_messages', '帳號不存在!') + res.redirect('/admin/signin') + } + req.flash('success_messages', '登入成功') + res.redirect('/admin/tweets') + }, + getTweets: (req, res) => { + Tweet.findAll({ + include: [ + User, + Like, + Reply + ] + }) + .then(Tweets => { + const data = Tweets.map(tweet => { + const newItem = tweet.toJSON(); + newItem.description = newItem.description.substring(0, 50) + newItem.LikeCount = newItem.Likes.length; + newItem.ReplyCount = newItem.Replies.length; + return newItem; + }); + res.render('admin/tweets', { tweets: data }) + }) + }, + deleteTweet: (req, res) => { + const { tweetId } = req.params + Tweet.findByPk(tweetId) + .then(tweet => { + if (!tweet) { + console.log('tweet不存在') + res.redirect('back') + } + req.flash("success_messages", "刪除成功!"); + return tweet.destroy() + }) + .then(() => res.redirect('/admin/tweets')) + }, + getUsers: (req, res) => { + User.findAll({ + include: [ + { model: Tweet, include: Like }, + { model: User, as: 'Followers' }, + { model: User, as: 'Followings' } + ], + order: [ + [sequelize.literal('(SELECT COUNT(*) FROM Tweets WHERE Tweets.UserId = User.id)'), 'DESC'] + ], + where: { role: 'user' } + }) + .then(users => { + const data = users.map(user => { + const newUser = user.toJSON() + newUser.likeCount = newUser.Tweets.reduce((totalLikes, tweet) => { + return totalLikes + tweet.Likes.length; + }, 0) + newUser.tweetCount = newUser.Tweets.length + newUser.followingCount = newUser.Followings.length + newUser.followerCount = newUser.Followers.length + return newUser + }) + res.render('admin/users', { users: data }) + }) + } +} + +module.exports = adminController \ No newline at end of file diff --git a/controllers/api-controller.js b/controllers/api-controller.js new file mode 100644 index 000000000..abbee5087 --- /dev/null +++ b/controllers/api-controller.js @@ -0,0 +1,70 @@ +const { User } = require('../models') +const helpers = require('../_helpers') +const { imgurFileHandler } = require('../helpers/file-helpers') + + const apiController = { + getUser: async (req, res, next) => { + try { + const currentUser = helpers.getUser(req) + const UserId = req.params.id + const user = await User.findOne({ + where: { id: UserId } + }) + if (currentUser.id !== user.id) throw new Error('無法觀看編輯其他使用者資料!') + + res.json(user.toJSON()) + } catch (err) { + req.flash('error_messages', err.message) + res.json({ status: 'error', message: err.message }); + next(err) + } + }, + putUser: async (req, res, next) => { + try { + const logInUserId = helpers.getUser(req).id + const UserId = req.params.id + const { name } = req.body + const introduction = req.body.introduction || '' + const avatar = req.files ? req.files.avatar : '' + const background = req.files ? req.files.background : '' + + let uploadAvatar = '' + let uploadBackground = '' + if (avatar) { + uploadAvatar = await imgurFileHandler(avatar[0]) + } + if (background) { + uploadBackground = await imgurFileHandler(background[0]) + } + const user = await User.findByPk(UserId) + if (!name) throw new Error('name不可空白') + if (name?.length > 50) throw new Error('name字數超出上限') + if (introduction?.length > 160) throw new Error('introduction字數超出上限') + if (user.id !== logInUserId) throw new Error('無法編輯別人的頁面') + + const data = await user.update({ + name, + introduction, + avatar: uploadAvatar || user.avatar, + background: uploadBackground || user.background + }) + res.json({ status: 'success', message: '已成功更新!', data }) + } catch (err) { + req.flash('error_messages', err.message) + res.json({ status: 'error', message: err.message }); + next(err) + } + }, + uploadImage: async (req, res) => { + const { files } = req + const uploadBackground = await imgurFileHandler(files?.background?.[0]) + const uploadAvatar = await imgurFileHandler(files?.avatar?.[0]) + res.json({ uploadBackground, uploadAvatar }) + }, + deleteImage: async (req, res) => { + const uploadBackground = helpers.getUser(req).background || DEFAULT_BACKGROUND + res.json({ uploadBackground }) + } +} + +module.exports = apiController \ No newline at end of file diff --git a/controllers/likes-controller.js b/controllers/likes-controller.js new file mode 100644 index 000000000..997227c8d --- /dev/null +++ b/controllers/likes-controller.js @@ -0,0 +1,64 @@ +const { Tweet, User, Reply, Like, Followship } = require('../models') +const { getEightRandomUsers } = require("../helpers/randomUsersHelper"); +const helpers = require('../_helpers') +const likeController = { + getLikes: async (req, res, next) => { + const isUser = helpers.getUser(req).id === Number(req.params.id) ? true : false + try { + const userId = req.params.id; + const currentUserId = helpers.getUser(req).id; + const user = await User.findByPk(userId,{ + include: [{ + model: Like, + include: [{ model: Tweet, + include:[User, Like, Reply]} + ] + }, + { model: User, as: 'Followers' }, + { model: User, as: 'Followings' } + ], + order: [["Likes","updatedAt", "DESC"]] + }); + + if (user) { + const userData = user.toJSON(); + const recommend = await getEightRandomUsers(req); + const isFollowed = user.Followers.some((l) => l.id === currentUserId); + const likedTweets = user.Likes.map(e => { + const replies = e.Tweet.Replies.length + const likes= e.Tweet.Likes.length + const isLiked = e.Tweet.Likes.some(l => l.UserId === currentUserId) + const userAvatar = e.Tweet.User.avatar; + return { + tweetId: e.Tweet.id, + userId: e.Tweet.User.id, + userAccount: e.Tweet.User.account, + userName: e.Tweet.User.name, + text: e.Tweet.description, + createdAt: e.Tweet.createdAt, + replies, + likes, + isLiked, + userAvatar + } + }) + const dataToRender = { + user: userData, + likedTweets, + recommend, + isUser, + isFollowed + }; + console.log(likedTweets) + res.render('user/user-likes', dataToRender); + } else { + res.status(404).send('未找到用户'); + } + } catch (err) { + console.error(err); + res.status(500).send("获取用户数据时出错。"); + } + } +} + +module.exports = likeController \ No newline at end of file diff --git a/controllers/reply-controller.js b/controllers/reply-controller.js new file mode 100644 index 000000000..cec46271b --- /dev/null +++ b/controllers/reply-controller.js @@ -0,0 +1,62 @@ +const { Tweet, User, Reply, Like, Followship } = require('../models') +const { getEightRandomUsers } = require("../helpers/randomUsersHelper"); +const helpers = require('../_helpers') + +const replyController = { + getReplies: async (req, res, next) => { + const isUser = helpers.getUser(req).id === Number(req.params.id) ? true : false + try { + const userId = req.params.id; + const currentUserId = req.user.id; + const user = await User.findByPk(userId,{ + include: [{ + model: Reply,include: [ + {model: Tweet, include: [{model: User}]}, + {model: User}] + }, + { model: User, as: 'Followers' }, + { model: User, as: 'Followings' } + ], + order: [["Replies", "updatedAt", "DESC"]] + }); + + if (user) { + const userData = user.toJSON(); + const recommend = await getEightRandomUsers(req); + const isFollowed = user.Followers.some((l) => l.id === currentUserId); + const replies = user.Replies.map(reply => ({ + User: { + account: reply.User.account, + name: reply.User.name, + id: reply.User.id, + avatar: reply.User.avatar || DEFAULT_AVATAR + }, + Tweet:{ + userAvatar: reply.Tweet.User.avatar, + username: reply.Tweet.User.name, + userId: reply.Tweet.User.id, + id: reply.Tweet.id + }, + comment: reply.comment, + createdAt: reply.createdAt + })); + const dataToRender = { + user: userData, + recommend, + isUser, + replies, + isFollowed + }; + + res.render('user/user-replies', dataToRender); + } else { + req.flash('error_messages', '未找到用戶') + res.redirect('back') + } + } catch (err) { + next(err) + } + } +} + +module.exports = replyController \ No newline at end of file diff --git a/controllers/tweets-controller.js b/controllers/tweets-controller.js new file mode 100644 index 000000000..912a7c933 --- /dev/null +++ b/controllers/tweets-controller.js @@ -0,0 +1,140 @@ +const { getEightRandomUsers } = require("../helpers/randomUsersHelper"); +const { Tweet, User, Reply, Like } = require("../models"); +const helpers = require("../_helpers"); + +const tweetsController = { + getTweets: async (req, res, next) => { + try { + const recommend = await getEightRandomUsers(req); + const currentUserId = helpers.getUser(req).id; + const currentUser = helpers.getUser(req); + const tweets = await Tweet.findAll({ + include: [User, Reply, Like], + order: [["updatedAt", "DESC"]], + }); + + const showTweets = tweets.map((tweet) => { + const replies = tweet.Replies.length; + const likes = tweet.Likes.length; + const isLiked = tweet.Likes.some((l) => l.UserId === currentUserId); + const userAvatar = tweet.User.avatar; + return { + tweetId: tweet.id, + userId: tweet.User.id, + userAccount: tweet.User.account, + userName: tweet.User.name, + userAvatar: tweet.User.avatar, + text: tweet.description, + createdAt: tweet.createdAt, + replies, + likes, + isLiked, + userAvatar, + currentUser, + }; + }); + return res.render("tweets", { + tweets: showTweets, + recommend, + currentUser, + }) + + + } catch (err) { + next(err); + } + }, + postTweet: async (req, res, next) => { + try { + const { description } = req.body; + const currentUserId = helpers.getUser(req).id; + if (!description) throw new Error("內容不可為空白"); + if (description.length > 140) throw new Error("內容不可超過140字"); + await Tweet.create({ + UserId: currentUserId, + description, + }); + req.flash("success_messages", "推文成功!"); + return res.redirect("back"); + } catch (err) { + next(err); + } + }, + getTweet: async (req, res, next) => { + try { + const recommend = await getEightRandomUsers(req); + const currentUserId = helpers.getUser(req).id; + const currentUser = helpers.getUser(req); + const { id } = req.params; + const tweet = await Tweet.findByPk(id, { + nest: true, + include: [User, { model: Reply, include: User }, Like], + }); + if (!tweet) throw new Error('推文不存在!') + const repliesAmount = tweet.Replies.length; + const likesAmount = tweet.Likes.length; + const isLiked = tweet.Likes.some((l) => l.UserId === currentUserId); + + return res.render("tweet", { + tweet: tweet.toJSON(), + repliesAmount, + likesAmount, + recommend, + isLiked, + currentUser, + }); + } catch (err) { + next(err); + } + }, + addLike: async (req, res, next) => { + try { + const { id } = req.params; + const currentUserId = helpers.getUser(req).id; + const likes = await Like.findOne({ + where: { userId: currentUserId, TweetId: id }, + }); + if (likes) throw new Error("You already liked this tweet!"); + await Like.create({ + UserId: currentUserId, + TweetId: id, + }); + return res.redirect("back"); + } catch (err) { + next(err); + } + }, + postUnlike: async (req, res, next) => { + try { + const { id } = req.params; + const currentUserId = helpers.getUser(req).id; + const likes = await Like.findOne({ + where: { userId: currentUserId, TweetId: id }, + }); + if (!likes) throw new Error("You already unlike it!"); + await likes.destroy(); + return res.redirect("back"); + } catch (err) { + next(err); + } + }, + postReply: async (req, res, next) => { + try { + const currentUserId = helpers.getUser(req).id; + const { comment } = req.body; + if (!comment) throw new Error("Content is required!"); + if (comment.length > 140) return; + await Reply.create({ + UserId: currentUserId, + TweetId: req.params.id, + comment, + }); + req.flash("success_messages", "留言成功!"); + return res.redirect("back"); + } catch (err) { + next(err); + } + }, +}; + +module.exports = tweetsController; diff --git a/controllers/user-controller.js b/controllers/user-controller.js new file mode 100644 index 000000000..dcc32ccf6 --- /dev/null +++ b/controllers/user-controller.js @@ -0,0 +1,336 @@ +const bcrypt = require("bcryptjs"); +const { Op } = require("sequelize"); +const { Tweet, User, Reply, Like, Followship, sequelize} = require("../models"); +const { getEightRandomUsers } = require("../helpers/randomUsersHelper"); +const helpers = require("../_helpers"); +const userController = { + signupPage: (req, res) => { + res.render("signup"); + }, + signup: (req, res, next) => { + const { account, name, email, password, checkPassword } = req.body; + const emailPromise = User.findOne({ where: { email } }); + const accountPromise = User.findOne({ where: { account } }); + let mailMsg = ""; + let accountMsg = ""; + let passwordMsg = ""; + let nameMsg = ""; + + if (!account || !name || !email || !password || !checkPassword) throw new Error('所有欄位皆為必填') + + return Promise.all([emailPromise, accountPromise]) + .then(([mailUser, accountUser]) => { + if (mailUser) { + mailMsg = "此信箱已被使用"; + } + if (accountUser) { + accountMsg = "此帳號已被使用"; + } + if (password !== checkPassword) { + passwordMsg = "密碼與確認密碼不相符"; + } + if (name.length > 50) { + nameMsg = '字數超出上限!' + } + if (mailMsg || accountMsg || passwordMsg || nameMsg) { + return res.render("signup", { + nameMsg, + passwordMsg, + mailMsg, + accountMsg, + account, + name, + email, + }); + } else { + bcrypt + .hash(password, 10) + .then((hashedPassword) => { + return User.create({ + account, + name, + email, + password: hashedPassword, + avatar: process.env.DEFAULT_AVATAR, + background: process.env.DEFAULT_BACKGROUND + }) + }) + .then(() => { + req.flash("success_messages", "註冊成功!"); + res.redirect("/signin"); + }); + } + } + ); + }, + signinPage: (req, res) => { + res.render("signin"); + }, + sigin: (req, res) => { + if (req.user.role === 'admin') { + req.flash('account_messages', '帳號不存在!') + res.redirect('/signin') + } + req.flash('success_messages', '成功登入!') + res.redirect("/tweets"); + }, + logout: (req, res) => { + req.flash('success_messages', '登出成功!') + req.logout(); + res.redirect("/signin"); + }, + postFollow: async (req, res, next) => { + + try { + const id = +req.body.id + if (+helpers.getUser(req).id === id) { + req.flash('error_messages', '不能跟隨自己!') + res.redirect(200, 'back')//配合test的expect(200) + //但這樣畫面會跳出ok. Redirecting to http://localhost:3000/users/${{id/followers}}"的頁面 + } else { + await Followship.create({ + followerId: helpers.getUser(req).id, + followingId: id + }) + req.flash("info_messages", "追蹤成功!"); + return res.redirect('back') + } + // const { followingUserId } = req.params; + // const currentUserId = req.user.id; + // const user = await User.findByPk(followingUserId); + // const followship = await Followship.findOne({ + // where: { followerId: currentUserId, followingId: followingUserId }, + // }); + // req.flash("info_messages", "追蹤成功!"); + // if (!user) throw new Error("User didn't exist"); + // if (followship) throw new Error("You are already following this user!"); + // await Followship.create({ + // followerId: currentUserId, + // followingId: followingUserId, + // }); + // res.redirect('back'); + } catch (err) { + next(err); + } + }, + deleteFollow: async (req, res, next) => { + try { + const currentUserId = helpers.getUser(req).id + const { followingUserId } = req.params; + const user = await User.findByPk(followingUserId); + const followship = await Followship.findOne({ + where: { followerId: currentUserId, followingId: followingUserId }, + }); + if (!user) throw new Error("User didn't exist"); + if (!followship) throw new Error("You aren't following this user!"); + await followship.destroy(); + req.flash("info_messages", "取消追蹤成功!"); + return res.redirect("back"); + } catch (err) { + next(err); + } + }, + getUser: async (req, res, next) => { + const isUser = + helpers.getUser(req).id === Number(req.params.id) ? true : false; + try { + const userId = req.params.id; + const currentUserId = helpers.getUser(req).id; + const user = await User.findByPk(userId, { + include: [ + { + model: Tweet, + include: [ + { model: User }, + { model: Reply, include: [{ model: Tweet }] }, + { model: Like }, + ] + }, + { model: User, as: "Followers" }, + { model: User, as: "Followings" }, + ], + order: [["Tweets","updatedAt", "DESC"]] + }); + + if (!user) throw new Error('使用者不存在') + const userData = user.toJSON(); + const recommend = await getEightRandomUsers(req); + + const isFollowed = user.Followers.some((l) => l.id === currentUserId); + + const tweets = user.Tweets.map((tweet) => { + const replies = tweet.Replies.length; + const likes = tweet.Likes.length; + const isLiked = tweet.Likes.some((l) => l.UserId === currentUserId); + const userAvatar = tweet.User.avatar; + return { + tweetId: tweet.id, + userId: tweet.User.id, + userAccount: tweet.User.account, + userName: tweet.User.name, + text: tweet.description, + createdAt: tweet.createdAt, + replies, + likes, + isLiked, + userAvatar, + }; + }); + + const dataToRender = { + user: userData,//這邊剛好命名是user 不是users + tweets, + recommend, + isUser, + isFollowed, + }; + + res.render("user/user-tweets", dataToRender); + } catch (err) { + next(err) + } + }, + getFollower: async (req, res, next) => { + // 跟隨者 + try { + const userId = req.params.id; + const user = await User.findByPk(userId, { + include: [ + { model: User, as: 'Followers', include: { model: User, as: 'Followers' } }, + { model: User, as: 'Followings', include: { model: User, as: 'Followers' } } + ], + order: [[sequelize.col('Followers.Followship.createdAt'), 'DESC']] + }); + + if (!user) throw new Error('使用者不存在') + const userData = user.toJSON(); + const recommend = await getEightRandomUsers(req); + const follows = user.Followers.map((e) => { + const isnotUser = e.id !== helpers.getUser(req).id; + const isFollowed = e.Followers.some( + (f) => f.id === helpers.getUser(req).id + ); + return { + id: e.id, + name: e.name, + avatar: e.avatar, + introduction: e.introduction ? e.introduction.substring(0, 50) : null, + isFollowed, + isnotUser, + }; + }); + + const dataToRender = { + user: userData, + recommend, + follows, + }; + + res.render("user/user-follower", dataToRender); + } catch (err) { + next(err) + } + }, + + getFollowing: async (req, res, next) => { + // 跟隨中 + try { + const userId = req.params.id; + const currentUserId = helpers.getUser(req).id; + const user = await User.findByPk(userId, { + include: [ + { model: User, as: 'Followers', include: { model: User, as: 'Followers' } }, + { model: User, as: 'Followings', include: { model: User, as: 'Followers' }} + ], + order: [[sequelize.col('Followings.Followship.createdAt'), 'DESC']] + }); + + if (!user) throw new Error('使用者不存在') + const userData = user.toJSON(); + const recommend = await getEightRandomUsers(req); + const follows = user.Followings.map((e) => { + const isnotUser = e.id !== helpers.getUser(req).id + const isFollowed = e.Followers.some(f => f.id === helpers.getUser(req).id) + return { + id: e.id, + name: e.name, + avatar: e.avatar, + introduction: e.introduction ? e.introduction.substring(0, 50) : null, + isFollowed, + isnotUser + }; + }); + const dataToRender = { + user: userData, + recommend, + follows, + }; + res.render("user/user-following", dataToRender); + } catch (err) { + next(err); + } + }, + getSetting: (req, res) => { + // 取得帳戶設定頁面 + return User.findByPk(helpers.getUser(req).id).then((user) => { + user = user.toJSON(); + const { name, account, email } = user; + return res.render("settings", { name, account, email }); + }); + }, + putSetting: (req, res, next) => { + const { account, name, email, password, checkPassword } = req.body; + const userId = req.user.id; + const emailPromise = User.findOne({ + where: { email, id: { [Op.ne]: userId } }, + }); + const accountPromise = User.findOne({ + where: { account, id: { [Op.ne]: userId } }, + }); + let mailMsg = ""; + let accountMsg = ""; + let passwordMsg = ""; + let nameMsg = ""; + + if (!account || !name || !email || !password || !checkPassword) throw new Error('所有欄位皆為必填') + + return Promise.all([emailPromise, accountPromise]) + .then(([mailUser, accountUser]) => { + if (mailUser) { + mailMsg = '此信箱已被使用' + } + if (accountUser) { + accountMsg = '此帳號已被使用' + } + if (password !== checkPassword) { + passwordMsg = '密碼與確認密碼不相符' + } + if (name.length > 50) { + nameMsg = '字數超出上限!' + } + if (mailMsg || accountMsg || passwordMsg || nameMsg) { + return res.render('settings', { nameMsg, mailMsg, accountMsg, passwordMsg, account, name, email }) + } else { + Promise.all([ + User.findByPk(userId), + bcrypt.hash(password, 10) + ]) + .then(([user, hashedPassword]) => { + return user.update({ + account, + name, + email, + password: hashedPassword, + }); + }) + .then(() => { + req.flash('success_messages', '編輯成功 !') + res.redirect('/settings') + }) + } + }) + .catch((err) => next(err)); + }, +}; + +module.exports = userController; diff --git a/helpers/file-helpers.js b/helpers/file-helpers.js new file mode 100644 index 000000000..0c69eddd9 --- /dev/null +++ b/helpers/file-helpers.js @@ -0,0 +1,30 @@ +const fs = require('fs') // 引入 fs 模組 +const imgur = require('imgur') +const IMGUR_CLIENT_ID = process.env.IMGUR_CLIENT_ID +imgur.setClientId(IMGUR_CLIENT_ID) + +const localFileHandler = file => { // file 是 multer 處理完的檔案 + return new Promise((resolve, reject) => { + if (!file) return resolve(null) + const fileName = `upload/${file.originalname}` + return fs.promises.readFile(file.path) + .then(data => fs.promises.writeFile(fileName, data)) + .then(() => resolve(`/${fileName}`)) + .catch(err => reject(err)) + }) +} +const imgurFileHandler = file => { + return new Promise((resolve, reject) => { + if (!file) return resolve(null) + return imgur.uploadFile(file.path) + .then(img => { + resolve(img?.link || null) // 檢查 img 是否存在 + }) + .catch(err => reject(err)) + }) +} + +module.exports = { + localFileHandler, + imgurFileHandler +} diff --git a/helpers/handlebars-helpers.js b/helpers/handlebars-helpers.js new file mode 100644 index 000000000..9583a0522 --- /dev/null +++ b/helpers/handlebars-helpers.js @@ -0,0 +1,15 @@ +const dayjs = require("dayjs"); +const relativeTime = require("dayjs/plugin/relativeTime"); +dayjs.extend(relativeTime); +module.exports = { + currentYear: () => dayjs().year(), + relativeTimeFromNow: (a) => dayjs(a).fromNow(), + hourTime: (a) => dayjs(a).format('A HH:mm'), + dateTime: (a) => dayjs(a).format('YYYY/MM/DD'), + ifCond: function (a, b, options) { + return a === b ? options.fn(this) : options.inverse(this); + }, + rawHelper: (options) => { + return options.fn(); + } +}; diff --git a/helpers/randomUsersHelper.js b/helpers/randomUsersHelper.js new file mode 100644 index 000000000..f372350c3 --- /dev/null +++ b/helpers/randomUsersHelper.js @@ -0,0 +1,45 @@ +const { User } = require("../models"); +const { Op } = require("sequelize"); +const helpers = require("../_helpers"); + +// 取得前八多追蹤人數的使用者 +async function getEightRandomUsers(req) { + try { + const currentUserId = helpers.getUser(req).id; + const users = await User.findAll({ + where: { id: { [Op.notIn]: [currentUserId] } }, //推薦清單排除跟自己 + include: [{ model: User, as: "Followers" }], + }); + const usersWithoutAdmin = await users.filter((user) => { + return user.dataValues.role !== "admin"; + }); //排除Admin + const eightRandomUsers = usersWithoutAdmin + .map((user) => { + return { + ...user.toJSON(), + followerCount: user.Followers.length, + }; + }) + .sort((a, b) => b.followerCount - a.followerCount) + .slice(0, 8); //排序並取出前八 + const recommend = eightRandomUsers.map((user) => { + const isFollowed = user.Followers.some( + (follower) => follower.id === currentUserId + ); + return { + id: user.id, + name: user.name, + avatar: user.avatar, + account: user.account, + isFollowed, + }; + }); + return recommend; + } catch (err) { + next(err) + } +} + +module.exports = { + getEightRandomUsers, +}; diff --git a/middleware/auth.js b/middleware/auth.js new file mode 100644 index 000000000..7300ec732 --- /dev/null +++ b/middleware/auth.js @@ -0,0 +1,24 @@ +const helpers = require('../_helpers') + +const authenticated = (req, res, next) => { + if (helpers.ensureAuthenticated(req)) { + if (helpers.getUser(req).role === 'user') return next() + if (helpers.getUser(req).role === 'admin') { + req.flash('error_messages', '管理者不能造訪前台頁面') + return res.redirect('/admin/tweets') + } + } + res.redirect('/signin') +} + +const authenticatedAdmin = (req, res, next) => { + if (helpers.ensureAuthenticated(req) && helpers.getUser(req).role === 'admin') { + return next() + } + res.redirect('/signin') +} + +module.exports = { + authenticated, + authenticatedAdmin +} \ No newline at end of file diff --git a/middleware/error-handler.js b/middleware/error-handler.js new file mode 100644 index 000000000..991aa2476 --- /dev/null +++ b/middleware/error-handler.js @@ -0,0 +1,11 @@ +module.exports = { + generalErrorHandler(err, req, res, next) { + if (err instanceof Error) { + req.flash("error_messages", `${err.name}: ${err.message}`); + } else { + req.flash("error_messages", `${err}`); + } + res.redirect("back"); + next(err); + }, +}; diff --git a/middleware/multer.js b/middleware/multer.js new file mode 100644 index 000000000..c38a2c818 --- /dev/null +++ b/middleware/multer.js @@ -0,0 +1,3 @@ +const multer = require('multer') +const upload = multer({ dest: 'temp/' }) +module.exports = upload \ No newline at end of file diff --git a/migrations/20190115071421-create-user.js b/migrations/20190115071421-create-user.js index 2376dbb50..da0cfad96 100644 --- a/migrations/20190115071421-create-user.js +++ b/migrations/20190115071421-create-user.js @@ -8,24 +8,31 @@ module.exports = { primaryKey: true, type: Sequelize.INTEGER }, - email: { + account: { type: Sequelize.STRING }, - password: { + name: { type: Sequelize.STRING }, - name: { + email: { type: Sequelize.STRING }, - avatar: { + password: { type: Sequelize.STRING }, introduction: { type: Sequelize.TEXT }, - role: { + avatar: { + type: Sequelize.STRING + }, + background: { type: Sequelize.STRING }, + role: { + type: Sequelize.STRING, + defaultValue: 'user' + }, createdAt: { allowNull: false, type: Sequelize.DATE diff --git a/models/followship.js b/models/followship.js index 790f3faa3..0d348bbab 100644 --- a/models/followship.js +++ b/models/followship.js @@ -1,8 +1,26 @@ -'use strict'; +'use strict' +const { + Model +} = require('sequelize') module.exports = (sequelize, DataTypes) => { - const Followship = sequelize.define('Followship', { - }, {}); - Followship.associate = function(models) { + class Followship extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } }; - return Followship; -}; \ No newline at end of file + Followship.init({ + followerId: DataTypes.INTEGER, + followingId: DataTypes.INTEGER + }, { + sequelize, + modelName: 'Followship', + tableName: 'Followships' + // underscored: true + }) + return Followship +} diff --git a/models/like.js b/models/like.js index c8939de1f..620ea3858 100644 --- a/models/like.js +++ b/models/like.js @@ -1,8 +1,24 @@ 'use strict'; +const { Model } = require('sequelize'); + module.exports = (sequelize, DataTypes) => { - const Like = sequelize.define('Like', { - }, {}); - Like.associate = function(models) { - }; + class Like extends Model { + static associate(models) { + Like.belongsTo(models.User, { foreignKey: 'UserId' }); + Like.belongsTo(models.Tweet, { foreignKey: 'TweetId' }); + } + } + Like.init( + { + UserId: DataTypes.INTEGER, + TweetId: DataTypes.INTEGER + }, + { + sequelize, + modelName: 'Like', + tableName: 'Likes', + } + ); + return Like; -}; \ No newline at end of file +}; diff --git a/models/reply.js b/models/reply.js index 60387f164..52539a2da 100644 --- a/models/reply.js +++ b/models/reply.js @@ -1,8 +1,28 @@ -'use strict'; +'use strict' +const { + Model +} = require('sequelize') module.exports = (sequelize, DataTypes) => { - const Reply = sequelize.define('Reply', { - }, {}); - Reply.associate = function(models) { + class Reply extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + Reply.belongsTo(models.Tweet, { foreignKey: 'TweetId' }) + Reply.belongsTo(models.User, { foreignKey: 'UserId' }) + } }; - return Reply; -}; \ No newline at end of file + Reply.init({ + comment: DataTypes.TEXT, + userId: DataTypes.INTEGER, + tweetId: DataTypes.INTEGER + }, { + sequelize, + modelName: 'Reply', + tableName: 'Replies' + // underscored: true + }) + return Reply +} diff --git a/models/tweet.js b/models/tweet.js index a8b660077..09ca07b07 100644 --- a/models/tweet.js +++ b/models/tweet.js @@ -1,8 +1,28 @@ -'use strict'; +"use strict"; +const { Model } = require("sequelize"); module.exports = (sequelize, DataTypes) => { - const Tweet = sequelize.define('Tweet', { - }, {}); - Tweet.associate = function(models) { - }; + class Tweet extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + Tweet.belongsTo(models.User, { foreignKey: "UserId" }); + Tweet.hasMany(models.Reply, { foreignKey: "TweetId" }); + Tweet.hasMany(models.Like, { foreignKey: "TweetId" }); + } + } + Tweet.init( + { + description: DataTypes.TEXT, + }, + { + sequelize, + modelName: "Tweet", + tableName: "Tweets", + // underscored: true + } + ); return Tweet; -}; \ No newline at end of file +}; diff --git a/models/user.js b/models/user.js index 82c5f84c8..9bd0961c1 100644 --- a/models/user.js +++ b/models/user.js @@ -1,8 +1,45 @@ -'use strict'; +"use strict"; +const { Model } = require("sequelize"); module.exports = (sequelize, DataTypes) => { - const User = sequelize.define('User', { - }, {}); - User.associate = function(models) { - }; + class User extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + User.hasMany(models.Reply, { foreignKey: "UserId" }); + User.hasMany(models.Tweet, { foreignKey: "UserId" }); + User.hasMany(models.Like, { foreignKey: "UserId" }); + User.belongsToMany(User, { + through: models.Followship, + foreignKey: "followingId", + as: "Followers", + }); + User.belongsToMany(User, { + through: models.Followship, + foreignKey: "followerId", + as: "Followings", + }); + } + } + User.init( + { + account: DataTypes.STRING, + name: DataTypes.STRING, + email: DataTypes.STRING, + password: DataTypes.STRING, + introduction: DataTypes.TEXT, + avatar: DataTypes.STRING, + background: DataTypes.STRING, + role: DataTypes.STRING, + }, + { + sequelize, + modelName: "User", + tableName: "Users", + // underscored: true + } + ); return User; -}; \ No newline at end of file +}; diff --git a/package-lock.json b/package-lock.json index 0307072c0..50d6b8e54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3183 +1,14 @@ { "name": "test", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 1, "requires": true, - "packages": { - "": { - "name": "test", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "bcrypt-nodejs": "0.0.3", - "body-parser": "^1.18.3", - "chai": "^4.2.0", - "connect-flash": "^0.1.1", - "express": "^4.16.4", - "express-handlebars": "^3.0.0", - "express-session": "^1.15.6", - "faker": "^4.1.0", - "method-override": "^3.0.0", - "mocha": "^6.0.2", - "mysql2": "^1.6.4", - "passport": "^0.4.0", - "passport-local": "^1.0.0", - "sequelize": "^6.18.0", - "sequelize-cli": "^5.5.0", - "sinon": "^10.0.0", - "sinon-chai": "^3.3.0" - }, - "devDependencies": { - "proxyquire": "^2.1.3", - "sequelize-test-helpers": "^1.4.2", - "supertest": "^3.3.0" - } - }, - "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/@sinonjs/samsam": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", - "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", - "dependencies": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" - } - }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" - }, - "node_modules/@types/debug": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", - "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" - }, - "node_modules/@types/node": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", - "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==" - }, - "node_modules/@types/validator": { - "version": "13.7.2", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.2.tgz", - "integrity": "sha512-KFcchQ3h0OPQgFirBRPZr5F/sVjxZsOrQHedj3zi8AH3Zv/hOLx2OLR4hxR5HcfoU+33n69ZuOfzthKVdMoTiw==" - }, - "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/ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "engines": { - "node": "*" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "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/bcrypt-nodejs": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz", - "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs=", - "deprecated": "bcrypt-nodejs is no longer actively maintained. Please use bcrypt or bcryptjs. See https://github.com/kelektiv/node.bcrypt.js/wiki/bcrypt-vs-brypt.js to learn more about these two options" - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "node_modules/body-parser": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", - "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", - "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.10.3", - "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/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/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" - }, - "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/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "loupe": "^2.3.1", - "pathval": "^1.1.1", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/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/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "engines": { - "node": "*" - } - }, - "node_modules/cli-color": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.4.0.tgz", - "integrity": "sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==", - "dependencies": { - "ansi-regex": "^2.1.1", - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "memoizee": "^0.4.14", - "timers-ext": "^0.1.5" - } - }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/connect-flash": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", - "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=", - "engines": { - "node": ">= 0.4.0" - } - }, - "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.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "engines": { - "node": ">= 0.6" - } - }, - "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/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/cookiejar": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", - "dev": true - }, - "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==", - "dev": true - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "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/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/denque": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz", - "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==", - "engines": { - "node": ">=0.10" - } - }, - "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/diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dottie": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", - "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" - }, - "node_modules/editorconfig": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", - "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", - "dependencies": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" - }, - "bin": { - "editorconfig": "bin/editorconfig" - } - }, - "node_modules/editorconfig/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/editorconfig/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-abstract": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", - "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", - "dependencies": { - "call-bind": "^1.0.2", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", - "get-symbol-description": "^1.0.0", - "has": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es5-ext": { - "version": "0.10.60", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.60.tgz", - "integrity": "sha512-jpKNXIt60htYG59/9FGf2PYT3pwMpnEbNKysU+k/4FGwyGtMotOvcZOuW+EmXXYASRqYSXQfGL5cVIthOTgbkg==", - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", - "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "node_modules/express": { - "version": "4.17.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", - "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.19.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.1.2", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.9.7", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.17.2", - "serve-static": "1.14.2", - "setprototypeof": "1.2.0", - "statuses": "~1.5.0", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express-handlebars": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.1.0.tgz", - "integrity": "sha512-7QlaXnSREMmN5P2o4gmpUZDfJlLtfBka9d6r7/ccXaU7rPp76odw9YYtwZYdIiha2JqwiaG6o2Wu6NZJQ0u7Fg==", - "dependencies": { - "glob": "^7.1.3", - "graceful-fs": "^4.1.2", - "handlebars": "^4.1.2", - "object.assign": "^4.1.0", - "promise": "^8.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/express-session": { - "version": "1.17.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", - "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", - "dependencies": { - "cookie": "0.4.1", - "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.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "on-finished": "~2.3.0", - "qs": "6.9.7", - "raw-body": "2.4.3", - "type-is": "~1.6.18" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", - "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "1.8.1", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", - "dependencies": { - "type": "^2.5.0" - } - }, - "node_modules/ext/node_modules/type": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz", - "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/faker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", - "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=" - }, - "node_modules/fill-keys": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz", - "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=", - "dev": true, - "dependencies": { - "is-object": "~1.0.1", - "merge-descriptors": "~1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/flat": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz", - "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==", - "dependencies": { - "is-buffer": "~2.0.3" - }, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", - "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", - "dev": true, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "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": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "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/generate-function": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", - "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "dependencies": { - "is-property": "^1.0.2" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "engines": { - "node": ">=4.x" - } - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "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-bigints": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", - "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "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/http-errors/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/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/inflection": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.2.tgz", - "integrity": "sha512-cmZlljCRTBFouT8UzMzrGcVEvkv6D/wBdcdKG7J1QH5cXjtU75Dm+P27v9EKu/Y43UYyCJd1WC4zLebRrC8NBw==", - "engines": [ - "node >= 0.4.0" - ] - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "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/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dependencies": { - "get-intrinsic": "^1.1.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, - "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "engines": { - "node": ">=4" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", - "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" - }, - "node_modules/is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "node_modules/js-beautify": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.3.tgz", - "integrity": "sha512-f1ra8PHtOEu/70EBnmiUlV8nJePS58y9qKjl4JHfYWlFH6bo7ogZBz//FAZp7jDuXtYnGYKymZPlrg2I/9Zo4g==", - "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^0.15.3", - "glob": "^7.1.3", - "nopt": "^5.0.0" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" - }, - "node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" - }, - "node_modules/log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", - "dependencies": { - "chalk": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "node_modules/loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", - "dependencies": { - "get-func-name": "^2.0.0" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "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/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "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.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" - }, - "node_modules/mkdirp": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", - "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", - "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", - "dependencies": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.4", - "ms": "2.1.1", - "node-environment-flags": "1.0.5", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "node_modules/mocha/node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/module-not-found-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz", - "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=", - "dev": true - }, - "node_modules/moment": { - "version": "2.29.3", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", - "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.5.34", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz", - "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==", - "dependencies": { - "moment": ">= 2.9.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/mysql2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.7.0.tgz", - "integrity": "sha512-xTWWQPjP5rcrceZQ7CSTKR/4XIDeH/cRkNH/uzvVGQ7W5c7EJ0dXeJUusk7OKhIoHj7uFKUxDVSCfLIl+jluog==", - "dependencies": { - "denque": "^1.4.1", - "generate-function": "^2.3.1", - "iconv-lite": "^0.5.0", - "long": "^4.0.0", - "lru-cache": "^5.1.1", - "named-placeholders": "^1.1.2", - "seq-queue": "^0.0.5", - "sqlstring": "^2.3.1" - }, - "engines": { - "node": ">= 8.0" - } - }, - "node_modules/mysql2/node_modules/iconv-lite": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", - "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/named-placeholders": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", - "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", - "dependencies": { - "lru-cache": "^4.1.3" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/named-placeholders/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/named-placeholders/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "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/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, - "node_modules/nise": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", - "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==", - "dependencies": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^6.0.0", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "node_modules/nise/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "node_modules/nise/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, - "node_modules/node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", - "dependencies": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - } - }, - "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/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.getownpropertydescriptors": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz", - "integrity": "sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" - }, - "engines": { - "node": ">= 0.8" - }, - "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": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "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.4.1", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz", - "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1" - }, - "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": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", - "dependencies": { - "passport-strategy": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "engines": { - "node": ">=4" - } - }, - "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "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": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "engines": { - "node": "*" - } - }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" - }, - "node_modules/pg-connection-string": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", - "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" - }, - "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==", - "dev": true - }, - "node_modules/promise": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", - "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", - "dependencies": { - "asap": "~2.0.6" - } - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" - }, - "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/proxyquire": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", - "integrity": "sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg==", - "dev": true, - "dependencies": { - "fill-keys": "^1.0.2", - "module-not-found-error": "^1.0.1", - "resolve": "^1.11.1" - } - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, - "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "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": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", - "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": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "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/readable-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==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/retry-as-promised": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-5.0.0.tgz", - "integrity": "sha512-6S+5LvtTl2ggBumk04hBo/4Uf6fRJUwIgunGZ7CYEBCeufGFW1Pu6ucUf/UskHeWOIsUcLOGLFXPig5tR5V1nA==" - }, - "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/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.17.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", - "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", - "dependencies": { - "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "1.8.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.3.0", - "range-parser": "~1.2.1", - "statuses": "~1.5.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/node_modules/destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "node_modules/send/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/send/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/send/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/seq-queue": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", - "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=" - }, - "node_modules/sequelize": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.19.0.tgz", - "integrity": "sha512-B3oGIdpYBERDjRDm74h7Ky67f6ZLcmBXOA7HscYObiOSo4pD7VBc9mtm44wNV7unc0uk8I1d30nbZBTQCE377A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/sequelize" - } - ], - "dependencies": { - "@types/debug": "^4.1.7", - "@types/validator": "^13.7.1", - "debug": "^4.3.3", - "dottie": "^2.0.2", - "inflection": "^1.13.2", - "lodash": "^4.17.21", - "moment": "^2.29.1", - "moment-timezone": "^0.5.34", - "pg-connection-string": "^2.5.0", - "retry-as-promised": "^5.0.0", - "semver": "^7.3.5", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.7.0", - "wkx": "^0.5.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependenciesMeta": { - "ibm_db": { - "optional": true - }, - "mariadb": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-hstore": { - "optional": true - }, - "snowflake-sdk": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "tedious": { - "optional": true - } - } - }, - "node_modules/sequelize-cli": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-5.5.1.tgz", - "integrity": "sha512-ZM4kUZvY3y14y+Rq3cYxGH7YDJz11jWHcN2p2x7rhAIemouu4CEXr5ebw30lzTBtyXV4j2kTO+nUjZOqzG7k+Q==", - "dependencies": { - "bluebird": "^3.5.3", - "cli-color": "^1.4.0", - "fs-extra": "^7.0.1", - "js-beautify": "^1.8.8", - "lodash": "^4.17.5", - "resolve": "^1.5.0", - "umzug": "^2.1.0", - "yargs": "^13.1.0" - }, - "bin": { - "sequelize": "lib/sequelize", - "sequelize-cli": "lib/sequelize" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/sequelize-pool": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", - "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/sequelize-test-helpers": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/sequelize-test-helpers/-/sequelize-test-helpers-1.4.2.tgz", - "integrity": "sha512-v7Yy9DKjzFA/OHLtxvFClgN2CKA9cRwxn9+6ha6xoqUzRngXdsbrmle0KD1onSqnCwVIweWlRTLJxcEl1ueozA==", - "dev": true, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/davesag" - }, - "peerDependencies": { - "chai": ">= 4", - "sinon": ">= 10.0.0" - } - }, - "node_modules/sequelize/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/sequelize/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/sequelize/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/sequelize/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sequelize/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/serve-static": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", - "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.17.2" - }, - "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": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "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/sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, - "node_modules/sinon": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-10.0.0.tgz", - "integrity": "sha512-XAn5DxtGVJBlBWYrcYKEhWCz7FLwZGdyvANRyK06419hyEpdT0dMc5A8Vcxg5SCGHc40CsqoKsc1bt1CbJPfNw==", - "dependencies": { - "@sinonjs/commons": "^1.8.1", - "@sinonjs/fake-timers": "^6.0.1", - "@sinonjs/samsam": "^5.3.1", - "diff": "^4.0.2", - "nise": "^4.1.0", - "supports-color": "^7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon-chai": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", - "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", - "peerDependencies": { - "chai": "^4.0.0", - "sinon": ">=4.0.0" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/sinon/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/sinon/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/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "node_modules/sqlstring": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/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==", - "dev": true - }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", - "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", - "dev": true, - "dependencies": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/superagent/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/superagent/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==", - "dev": true - }, - "node_modules/supertest": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.4.2.tgz", - "integrity": "sha512-WZWbwceHUo2P36RoEIdXvmqfs47idNNZjCuJOqDz6rvtkk8ym56aU5oglORCpPeXGxT7l9rkJ41+O1lffQXYSA==", - "dev": true, - "dependencies": { - "methods": "^1.1.2", - "superagent": "^3.8.3" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", - "dependencies": { - "es5-ext": "~0.10.46", - "next-tick": "1" - } - }, - "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/toposort-class": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", - "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" - }, - "node_modules/type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "engines": { - "node": ">=4" - } - }, - "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/uglify-js": { - "version": "3.15.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", - "integrity": "sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "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/umzug": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", - "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", - "dependencies": { - "bluebird": "^3.7.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", - "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "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": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "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==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wkx": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", - "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - }, - "node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "node_modules/yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dependencies": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - } - }, "dependencies": { + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==" + }, "@sinonjs/commons": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", @@ -3209,6 +40,25 @@ "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==" }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -3217,6 +67,19 @@ "@types/ms": "*" } }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==" + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "requires": { + "@types/node": "*" + } + }, "@types/ms": { "version": "0.7.31", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", @@ -3227,6 +90,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.24.tgz", "integrity": "sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g==" }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "requires": { + "@types/node": "*" + } + }, "@types/validator": { "version": "13.7.2", "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.2.tgz", @@ -3264,6 +135,11 @@ "color-convert": "^1.9.0" } }, + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3290,8 +166,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "balanced-match": { "version": "1.0.2", @@ -3303,6 +178,11 @@ "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz", "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs=" }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -3341,11 +221,67 @@ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, + "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==" + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + } + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" + }, + "cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3447,6 +383,14 @@ } } }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "requires": { + "mimic-response": "^1.0.0" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3464,7 +408,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -3485,6 +428,17 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "config-chain": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", @@ -3531,8 +485,7 @@ "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==", - "dev": true + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "d": { "version": "1.0.1", @@ -3543,6 +496,11 @@ "type": "^1.0.1" } }, + "dayjs": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.6.tgz", + "integrity": "sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3556,6 +514,21 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", @@ -3564,6 +537,11 @@ "type-detect": "^4.0.0" } }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" + }, "define-properties": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", @@ -3576,8 +554,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "denque": { "version": "1.5.1", @@ -3594,11 +571,48 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + } + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, "dottie": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", @@ -3646,6 +660,14 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, "es-abstract": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", @@ -4034,6 +1056,14 @@ "has-symbols": "^1.0.1" } }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, "get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -4056,6 +1086,24 @@ "path-is-absolute": "^1.0.0" } }, + "got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -4122,6 +1170,11 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" + }, "http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -4141,6 +1194,15 @@ } } }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4149,6 +1211,33 @@ "safer-buffer": ">= 2.1.2 < 3" } }, + "imgur": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/imgur/-/imgur-1.0.2.tgz", + "integrity": "sha512-bZJkRpa3ReR7lSEzAOjO4PPl9OIDQPuiKoG2aOh36PrTBQCrZL/oTcc6VClyyXEg9O6rEMpsuCloxfhqybpfZA==", + "requires": { + "commander": "^7.1.0", + "form-data": "^4.0.0", + "got": "^11.8.1" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "inflection": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.2.tgz", @@ -4309,8 +1398,7 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -4337,6 +1425,11 @@ "esprima": "^4.0.0" } }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -4350,6 +1443,14 @@ "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" }, + "keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "requires": { + "json-buffer": "3.0.1" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -4390,6 +1491,11 @@ "get-func-name": "^2.0.0" } }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4475,6 +1581,11 @@ "mime-db": "1.52.0" } }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4597,6 +1708,21 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "multer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.3.tgz", + "integrity": "sha512-np0YLKncuZoTzufbkM6wEKp68EhWJXcU6fq6QqrSwkckd2LlMgd1UqhUJLj6NS/5sZ8dE8LYDWslsltJznnXlg==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, "mysql2": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-1.7.0.tgz", @@ -4705,6 +1831,16 @@ "abbrev": "1" } }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "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==" + }, "object-inspect": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", @@ -4757,6 +1893,11 @@ "wrappy": "1" } }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==" + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -4843,8 +1984,7 @@ "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==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "promise": { "version": "8.1.0", @@ -4884,6 +2024,15 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "qs": { "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", @@ -4892,6 +2041,11 @@ "side-channel": "^1.0.4" } }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -4917,7 +2071,6 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -4931,8 +2084,7 @@ "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==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -4956,6 +2108,19 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "requires": { + "lowercase-keys": "^2.0.0" + } + }, "retry-as-promised": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-5.0.0.tgz", @@ -5121,8 +2286,7 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/sequelize-test-helpers/-/sequelize-test-helpers-1.4.2.tgz", "integrity": "sha512-v7Yy9DKjzFA/OHLtxvFClgN2CKA9cRwxn9+6ha6xoqUzRngXdsbrmle0KD1onSqnCwVIweWlRTLJxcEl1ueozA==", - "dev": true, - "requires": {} + "dev": true }, "serve-static": { "version": "1.14.2", @@ -5196,8 +2360,7 @@ "sinon-chai": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-3.7.0.tgz", - "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==", - "requires": {} + "integrity": "sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g==" }, "source-map": { "version": "0.6.1", @@ -5219,22 +2382,10 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "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==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "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==", - "dev": true - } - } + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==" }, "string-width": { "version": "2.1.1", @@ -5263,6 +2414,21 @@ "define-properties": "^1.1.3" } }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "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==" + } + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -5341,6 +2507,11 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "sweetalert2": { + "version": "11.7.27", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.27.tgz", + "integrity": "sha512-QbRXGQn1sb7HEhzA/K2xtWIwQHh/qkSbb1w6jYcTql2xy17876lTREEt1D4X6Q0x2wHtfUjKJ+Cb8IVkRoq7DQ==" + }, "timers-ext": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", @@ -5379,6 +2550,11 @@ "mime-types": "~2.1.24" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "uglify-js": { "version": "3.15.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.15.4.tgz", @@ -5425,8 +2601,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", @@ -5534,6 +2709,11 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, "y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", diff --git a/package.json b/package.json index e85b8c07c..afb65e185 100644 --- a/package.json +++ b/package.json @@ -4,30 +4,36 @@ "description": "", "main": "index.js", "scripts": { - "start": "NODE_ENV=development node app.js", - "dev": "NODE_ENV=development nodemon app.js", - "test": "mocha test --exit --recursive --timeout 5000" + "start": "set \"NODE_ENV=development\" && node app.js", + "dev": "set \"NODE_ENV=development\" && nodemon app.js", + "test": "set \"NODE_ENV=test\" && mocha test --exit --recursive --timeout 5000" }, "author": "", "license": "ISC", "dependencies": { "bcrypt-nodejs": "0.0.3", + "bcryptjs": "^2.4.3", "body-parser": "^1.18.3", "chai": "^4.2.0", "connect-flash": "^0.1.1", + "dayjs": "^1.10.6", + "dotenv": "^10.0.0", "express": "^4.16.4", "express-handlebars": "^3.0.0", "express-session": "^1.15.6", "faker": "^4.1.0", + "imgur": "^1.0.2", "method-override": "^3.0.0", "mocha": "^6.0.2", + "multer": "^1.4.3", "mysql2": "^1.6.4", "passport": "^0.4.0", "passport-local": "^1.0.0", "sequelize": "^6.18.0", "sequelize-cli": "^5.5.0", "sinon": "^10.0.0", - "sinon-chai": "^3.3.0" + "sinon-chai": "^3.3.0", + "sweetalert2": "^11.7.27" }, "devDependencies": { "proxyquire": "^2.1.3", diff --git a/public/images/admin/following.svg b/public/images/admin/following.svg new file mode 100644 index 000000000..75ef652f7 --- /dev/null +++ b/public/images/admin/following.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/admin/tweets-active.svg b/public/images/admin/tweets-active.svg new file mode 100644 index 000000000..288408213 --- /dev/null +++ b/public/images/admin/tweets-active.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/admin/tweets.svg b/public/images/admin/tweets.svg new file mode 100644 index 000000000..4cc5d813e --- /dev/null +++ b/public/images/admin/tweets.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/admin/users-active.svg b/public/images/admin/users-active.svg new file mode 100644 index 000000000..5b2570774 --- /dev/null +++ b/public/images/admin/users-active.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/admin/users.svg b/public/images/admin/users.svg new file mode 100644 index 000000000..44e5ccd91 --- /dev/null +++ b/public/images/admin/users.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/alert-error.svg b/public/images/alert-error.svg new file mode 100644 index 000000000..9493f43b0 --- /dev/null +++ b/public/images/alert-error.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/alert-info.svg b/public/images/alert-info.svg new file mode 100644 index 000000000..8c63cc629 --- /dev/null +++ b/public/images/alert-info.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/alert-success.svg b/public/images/alert-success.svg new file mode 100644 index 000000000..9f5ae8663 --- /dev/null +++ b/public/images/alert-success.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/alert-warning.svg b/public/images/alert-warning.svg new file mode 100644 index 000000000..1e0de6cea --- /dev/null +++ b/public/images/alert-warning.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/arrow-left.svg b/public/images/arrow-left.svg new file mode 100644 index 000000000..9b7e432f5 --- /dev/null +++ b/public/images/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/bell-active.svg b/public/images/bell-active.svg new file mode 100644 index 000000000..83d3a626e --- /dev/null +++ b/public/images/bell-active.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/bell.svg b/public/images/bell.svg new file mode 100644 index 000000000..bc46fe18e --- /dev/null +++ b/public/images/bell.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/camera.svg b/public/images/camera.svg new file mode 100644 index 000000000..e9ed61d03 --- /dev/null +++ b/public/images/camera.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/cancel.svg b/public/images/cancel.svg new file mode 100644 index 000000000..b056033de --- /dev/null +++ b/public/images/cancel.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/default_avatar.png b/public/images/default_avatar.png new file mode 100644 index 000000000..2ffba94b0 Binary files /dev/null and b/public/images/default_avatar.png differ diff --git a/public/images/default_background.png b/public/images/default_background.png new file mode 100644 index 000000000..f7da90e93 Binary files /dev/null and b/public/images/default_background.png differ diff --git a/public/images/envelope.svg b/public/images/envelope.svg new file mode 100644 index 000000000..58fc6809b --- /dev/null +++ b/public/images/envelope.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/home-active.svg b/public/images/home-active.svg new file mode 100644 index 000000000..9afe4bd34 --- /dev/null +++ b/public/images/home-active.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/home.svg b/public/images/home.svg new file mode 100644 index 000000000..a31641bc8 --- /dev/null +++ b/public/images/home.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/icon-close.svg b/public/images/icon-close.svg new file mode 100644 index 000000000..824716a5c --- /dev/null +++ b/public/images/icon-close.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icon-delete.svg b/public/images/icon-delete.svg new file mode 100644 index 000000000..ef68882a2 --- /dev/null +++ b/public/images/icon-delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icon-like-active.svg b/public/images/icon-like-active.svg new file mode 100644 index 000000000..de56e6668 --- /dev/null +++ b/public/images/icon-like-active.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/icon-like.svg b/public/images/icon-like.svg new file mode 100644 index 000000000..0f6ece96e --- /dev/null +++ b/public/images/icon-like.svg @@ -0,0 +1,9 @@ + + + + diff --git a/public/images/like.svg b/public/images/like.svg new file mode 100644 index 000000000..cc2528848 --- /dev/null +++ b/public/images/like.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/logo.svg b/public/images/logo.svg new file mode 100644 index 000000000..94c30fceb --- /dev/null +++ b/public/images/logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/logout-active.svg b/public/images/logout-active.svg new file mode 100644 index 000000000..a1aede070 --- /dev/null +++ b/public/images/logout-active.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/logout.svg b/public/images/logout.svg new file mode 100644 index 000000000..9539506d1 --- /dev/null +++ b/public/images/logout.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/profile-active.svg b/public/images/profile-active.svg new file mode 100644 index 000000000..206b27e26 --- /dev/null +++ b/public/images/profile-active.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/profile.svg b/public/images/profile.svg new file mode 100644 index 000000000..a37c43d5d --- /dev/null +++ b/public/images/profile.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/reply-icon.svg b/public/images/reply-icon.svg new file mode 100644 index 000000000..2cbf17ac1 --- /dev/null +++ b/public/images/reply-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/setting-active.svg b/public/images/setting-active.svg new file mode 100644 index 000000000..93bb2a19d --- /dev/null +++ b/public/images/setting-active.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/setting.svg b/public/images/setting.svg new file mode 100644 index 000000000..3467affd9 --- /dev/null +++ b/public/images/setting.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/tweet-btn.svg b/public/images/tweet-btn.svg new file mode 100644 index 000000000..fc321e858 --- /dev/null +++ b/public/images/tweet-btn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/javascripts/deleteConfirm.js b/public/javascripts/deleteConfirm.js new file mode 100644 index 000000000..b44f8fcdf --- /dev/null +++ b/public/javascripts/deleteConfirm.js @@ -0,0 +1,24 @@ +const tweets = document.querySelector('.li-container') + +function deleteTweet(event) { + const target = event.target + + if ((target.matches('img'))) { + event.preventDefault() + + swal({ + title: "確定刪除資料?", + icon: "warning", + text: "刪除的資料將無法恢復", + buttons: true, + dangerMode: true + }).then(check => { + if (check) { + const id = target.dataset.id + document.querySelector(`.form-${id}`).submit() + } + }) + } +} + +tweets.addEventListener('click', (deleteTweet)) \ No newline at end of file diff --git a/public/javascripts/editModal.js b/public/javascripts/editModal.js new file mode 100644 index 000000000..20834630f --- /dev/null +++ b/public/javascripts/editModal.js @@ -0,0 +1,86 @@ +const editModal = document.querySelector('#editModal') +const introductionCount = document.querySelector('#introduction-area') +const nameCount = document.querySelector('#name-area') +const inputName = document.querySelector('#name') +const inputIntroduction = document.querySelector('#introduction') +const editButton = document.querySelector('#editButton') +const backgroundImage = document.querySelector('#background-image') +const deleteBgButton = document.querySelector('.edit-x-icon') +const inputBackground = document.querySelector('#background') +const avatarImage = document.querySelector('#avatar-image') +const inputAvatar = document.querySelector('#avatar') + +inputBackground.addEventListener('change', async () => { + try{ + const formData = new FormData() + formData.append('background', inputBackground.files[0]) + const data = await axios.post(`/api/image`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + backgroundPath = data.data.uploadBackground + backgroundImage.src = backgroundPath + } catch (err) { console.log(err) } +}) + + +deleteBgButton.addEventListener('click', async () => { + try { + const data = await axios.post(`/api/imagex`) + backgroundPath = data.data.uploadBackground + backgroundImage.src = backgroundPath + inputBackground.value = '' + } catch (err) { console.log(err) } +}) + +inputAvatar.addEventListener('change', async () => { + try { + const formData = new FormData() + formData.append('avatar', inputAvatar.files[0]) + const data = await axios.post(`/api/image`, formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + avatarPath = data.data.uploadAvatar + avatarImage.src = avatarPath + } catch (err) { console.log(err) } +}) + +// 儲存modal資訊,刷新頁面 +editModal.addEventListener('submit', function (event) { + event.preventDefault() + const target = event.target + const UserId = target.dataset.id + const formData = new FormData() + formData.append('name', inputName.value) + formData.append('introduction', inputIntroduction.value) + formData.append('background', inputBackground.files[0]) + formData.append('avatar', inputAvatar.files[0]) + axios + .post(`/api/users/${UserId}`, formData, { + headers: { 'Content-Type': 'multipart/form-data' } + }) + .then(res => { + history.go(0) // 刷新本頁 + }) + .catch(err => console.log(err)) +}) + +function introWordsCheck () { + const content = document.querySelector('#introduction').value.trim().length + if (content === 160) { + introductionCount.innerHTML = `字數不可超過${content}字` + } else { + introductionCount.innerHTML = `

${content}/160

` + } +} +function nameWordsCheck () { + const content = document.querySelector('#name').value.trim().length + if (content === 50) { + nameCount.innerHTML = `字數不可超過${content}字` + } else { + nameCount.innerHTML = `

${content}/50

` + } +} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 000000000..a76e77b9b --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,119 @@ +.nav-link { + color: #657786; + border: none; +} + +.nav-link:hover, +.nav-link.active { + color: #ff6600 !important; + border: none !important; + border-bottom: 1px solid #ff6600 !important; +} + +.user-edit-button { + background: #ffffff; + color: #ff6600; + border-radius: 100px; + border: 1px solid #ff6600; + font-size: 16px; + line-height: 24px; +} + +.user-edit-button:hover { + background-color: #ff6600; + color: white; + border-radius: 100px; + border: 1px solid #ff6600; + font-size: 16px; + line-height: 24px; +} + + +.reply-btn, +.like-btn { + width: 40px; + height: 40px; + border-radius: 50%; + border: none; + background: none; + display: flex; + justify-content: center; + align-items: center; + transition: background-color 0.2s ease-in; +} + +.follow-btn{ + padding: 0; margin: 0px; + cursor: pointer; + color:#FF6600; + border:1px solid #FF6600; + width:64px; + height:40px; + border-radius:50px; + background: none; +} + +.follow-btn:hover{ + padding: 0; margin: 0px; + cursor: pointer; + color:white; + border:1px solid #FF6600; + width:64px; + height:40px; + border-radius:50px; + background-color: #ff6600; +} + +.follow-btn-active{ + padding: 0; + margin: 0px; + cursor: pointer; + color:white; + background-color:#FF6600; + width:96px; + height:40px; + border-radius:50px; + border:1px solid #FF6600; +} + +.follow-btn-active:hover{ + padding: 0; + margin: 0px; + cursor: pointer; + color:white; + background-color:#ff660094; + width:96px; + height:40px; + border-radius:50px; + border: none; +} + +.reply-btn:hover { + background-color: lightblue; +} + +.like-btn:hover { + background-color: lightcoral; +} + +.user-text-link { + text-decoration: none; + color: #000000; +} + +.user-text-link:hover { + text-decoration: underline; +} + +.tweet-user-text-link{ + text-decoration: none; + color: #FF6600; + } + +.tweet-user-text-link:hover{ + text-decoration: underline; + } + + .like-form, form { + margin-bottom:0 + } \ No newline at end of file diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 000000000..b875503b8 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,72 @@ +const express = require("express"); +const router = express.Router(); +const passport = require("../config/passport"); +const upload = require("../middleware/multer"); + +const tweetsController = require('../controllers/tweets-controller') +const userController = require('../controllers/user-controller') +const replyController = require('../controllers/reply-controller') +const likesController = require('../controllers/likes-controller') +const apiController = require('../controllers/api-controller') + +const { authenticated, authenticatedAdmin } = require("../middleware/auth"); +const { generalErrorHandler } = require("../middleware/error-handler"); +const admin = require("./modules/admin"); + +router.use("/admin", admin); + +router.get("/signup", userController.signupPage); +router.post("/signup", userController.signup); +router.get("/signin", userController.signinPage); +router.post( + "/signin", + passport.authenticate("local", { failureRedirect: "/signin" }), + userController.sigin +); +router.get("/logout", userController.logout); +router.get("/tweets", authenticated, tweetsController.getTweets); +router.post("/tweets", authenticated, tweetsController.postTweet); +router.get("/tweets/:id/replies", authenticated, tweetsController.getTweet); +router.post( + "/followships", + authenticated, + userController.postFollow +); +router.delete( + "/followships/:followingUserId", + authenticated, + userController.deleteFollow +); +router.post("/tweets/:id/like", authenticated, tweetsController.addLike); +router.post( + "/tweets/:id/unlike", + authenticated, + tweetsController.postUnlike +); +router.post( + "/tweets/:id/replies", + authenticated, + tweetsController.postReply +); +router.get("/tweets", authenticated, tweetsController.getTweets); +router.post("/tweets", authenticated, tweetsController.postTweet); +router.post("/users/:followingUserId/follow", userController.postFollow); + +router.get("/users/:id/tweets", authenticated, userController.getUser); +router.get("/users/:id/replies", authenticated, replyController.getReplies); +router.get("/users/:id/likes", authenticated, likesController.getLikes); +router.get("/users/:id/followers", authenticated, userController.getFollower); +router.get("/users/:id/followings", authenticated, userController.getFollowing); +router.get('/api/users/:id', authenticated, apiController.getUser) +router.post('/api/users/:id', upload.fields([{ name: 'background', maxCount: 1 }, { name: 'avatar', maxCount: 1 }]), authenticated, apiController.putUser) +router.post('/api/image', upload.fields([ + { name: 'background' }, + { name: 'avatar' } +]), authenticated, apiController.uploadImage) +router.post('/api/imagex', authenticated, apiController.deleteImage) +router.get("/settings", authenticated, userController.getSetting); // 個人資料設定 +router.put("/settings", authenticated, userController.putSetting); // 個人資料編輯 +router.use('/', (req, res) => res.redirect('/tweets')); +router.use("/", generalErrorHandler); + +module.exports = router; diff --git a/routes/modules/admin.js b/routes/modules/admin.js new file mode 100644 index 000000000..f3dded9e4 --- /dev/null +++ b/routes/modules/admin.js @@ -0,0 +1,17 @@ +const express = require('express') +const router = express.Router() +const passport = require('../../config/passport') + +const { authenticated, authenticatedAdmin } = require('../../middleware/auth') + +const adminController = require('../../controllers/admin-controller') + +router.get('/signin', adminController.signinPage) +router.post('/signin', passport.authenticate('local', { failureRedirect: '/admin/signin' }), adminController.signin) + +router.get('/tweets', authenticatedAdmin, adminController.getTweets) +router.delete('/tweets/:tweetId', authenticatedAdmin, adminController.deleteTweet) + +router.get('/users', authenticatedAdmin, adminController.getUsers) + +module.exports = router \ No newline at end of file diff --git a/seeders/20230821041106-user-seed-file.js b/seeders/20230821041106-user-seed-file.js new file mode 100644 index 000000000..b629aea0e --- /dev/null +++ b/seeders/20230821041106-user-seed-file.js @@ -0,0 +1,56 @@ +'use strict' +const bcrypt = require('bcryptjs') + +const DEFAULT_AVATAR = 'https://i.imgur.com/FUerPDO.png' +const DEFAULT_BACKGROUND = 'https://i.imgur.com/IkTFZsQ.png' +const userCount = 20 + +module.exports = { + up: async (queryInterface, Sequelize) => { + const userPromises = []; + + // 創建管理用戶 + const adminHashedPassword = await bcrypt.hash('12345678', 10); + const adminUser = { + name: 'root', + account: 'root', + email: 'root@example.com', + avatar: DEFAULT_AVATAR, + background: DEFAULT_BACKGROUND, + role: 'admin', + createdAt: new Date(), + updatedAt: new Date(), + password: adminHashedPassword + }; + userPromises.push(adminUser); + + // 創建基本用戶 + for (let i = 1; i <= userCount; i++) { + const hashedPassword = await bcrypt.hash('12345678', 10); + + const user = { + name: `user${i}`, + account: `user${i}`, + email: `user${i}@example.com`, + avatar: DEFAULT_AVATAR, + background: DEFAULT_BACKGROUND, + role: 'user', + createdAt: new Date(), + updatedAt: new Date(), + password: hashedPassword + }; + + userPromises.push(user); + } + + try { + await queryInterface.bulkInsert('Users', userPromises) + } catch (error) { + console.error(error) + } + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('Users', null, {}) + } +} diff --git a/seeders/20230821062212-tweet-seed-file.js b/seeders/20230821062212-tweet-seed-file.js new file mode 100644 index 000000000..b9450e939 --- /dev/null +++ b/seeders/20230821062212-tweet-seed-file.js @@ -0,0 +1,35 @@ +'use strict' +const faker = require('faker') + +module.exports = { + up: async (queryInterface, Sequelize) => { + const users = await queryInterface.sequelize.query( + 'SELECT id FROM Users WHERE email != \'root@example.com\'', + { type: queryInterface.sequelize.QueryTypes.SELECT } + ); + + + + const tweetData = [] + + users.map(user => { + for (let i = 0; i < 10; i++) { + const tweet = { + UserId: user.id, + description: faker.lorem.text(), + createdAt: new Date(), + updatedAt: new Date() + } + tweetData.push(tweet) + } + }) + + await queryInterface.bulkInsert( + 'Tweets', + tweetData + ) + }, + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('Tweets', {}) + } +} diff --git a/seeders/20230821064930-Reply-seed-file.js b/seeders/20230821064930-Reply-seed-file.js new file mode 100644 index 000000000..251934dc4 --- /dev/null +++ b/seeders/20230821064930-Reply-seed-file.js @@ -0,0 +1,35 @@ +'use strict' +const faker = require('faker') + +module.exports = { + up: async (queryInterface, Sequelize) => { + const users = await queryInterface.sequelize.query('SELECT id FROM Users WHERE email !="root@example.com"', + { type: queryInterface.sequelize.QueryTypes.SELECT }) + const tweets = await queryInterface.sequelize.query('SELECT id FROM Tweets', + { type: queryInterface.sequelize.QueryTypes.SELECT }) + + const replyData = [] + + tweets.map(tweet => { + for (let i = 0; i < 3; i++) { + const reply = { + TweetId: tweet.id, + UserId: users[Math.floor(Math.random() * users.length)].id, + comment: faker.lorem.text().substring(0, 45), + createdAt: new Date(), + updatedAt: new Date() + } + replyData.push(reply) + } + }) + + await queryInterface.bulkInsert( + 'Replies', + replyData + ) + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.bulkDelete('Replies', {}) + } +} diff --git a/temp/00732efecc2e5b9947b840d6c38d1443 b/temp/00732efecc2e5b9947b840d6c38d1443 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/00732efecc2e5b9947b840d6c38d1443 differ diff --git a/temp/025297b08622676d46e2a727ddcb8ff2 b/temp/025297b08622676d46e2a727ddcb8ff2 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/025297b08622676d46e2a727ddcb8ff2 differ diff --git a/temp/10cb1e1d65050478ca36307af6f71fcd b/temp/10cb1e1d65050478ca36307af6f71fcd new file mode 100644 index 000000000..2614cb1b9 Binary files /dev/null and b/temp/10cb1e1d65050478ca36307af6f71fcd differ diff --git a/temp/2b0f0ffe64b28290f888f079d84f009d b/temp/2b0f0ffe64b28290f888f079d84f009d new file mode 100644 index 000000000..fc714d3f0 Binary files /dev/null and b/temp/2b0f0ffe64b28290f888f079d84f009d differ diff --git a/temp/32da30c6cef51bd7dae08c2115ef6dd5 b/temp/32da30c6cef51bd7dae08c2115ef6dd5 new file mode 100644 index 000000000..f76f06c61 Binary files /dev/null and b/temp/32da30c6cef51bd7dae08c2115ef6dd5 differ diff --git a/temp/330e466a97d266f89478295e979ea24e b/temp/330e466a97d266f89478295e979ea24e new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/330e466a97d266f89478295e979ea24e differ diff --git a/temp/3d97939f1cea914e1718e0bc41004798 b/temp/3d97939f1cea914e1718e0bc41004798 new file mode 100644 index 000000000..f76f06c61 Binary files /dev/null and b/temp/3d97939f1cea914e1718e0bc41004798 differ diff --git a/temp/40f153e1855490e98b9786344c1ab97d b/temp/40f153e1855490e98b9786344c1ab97d new file mode 100644 index 000000000..fc714d3f0 Binary files /dev/null and b/temp/40f153e1855490e98b9786344c1ab97d differ diff --git a/temp/4634f284141c2adc9502d87ed1695c1b b/temp/4634f284141c2adc9502d87ed1695c1b new file mode 100644 index 000000000..f76f06c61 Binary files /dev/null and b/temp/4634f284141c2adc9502d87ed1695c1b differ diff --git a/temp/5bf23de1f5aaf1bc01020bcf2f3c9233 b/temp/5bf23de1f5aaf1bc01020bcf2f3c9233 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/5bf23de1f5aaf1bc01020bcf2f3c9233 differ diff --git a/temp/6331fad8e7f44c271ebaeb6091c946ec b/temp/6331fad8e7f44c271ebaeb6091c946ec new file mode 100644 index 000000000..2614cb1b9 Binary files /dev/null and b/temp/6331fad8e7f44c271ebaeb6091c946ec differ diff --git a/temp/6a30f4f3285d33d8f0672fe399789452 b/temp/6a30f4f3285d33d8f0672fe399789452 new file mode 100644 index 000000000..7e9e4c07c Binary files /dev/null and b/temp/6a30f4f3285d33d8f0672fe399789452 differ diff --git a/temp/7abef4f243b372619179eb9cacd8a20a b/temp/7abef4f243b372619179eb9cacd8a20a new file mode 100644 index 000000000..c66af9f50 Binary files /dev/null and b/temp/7abef4f243b372619179eb9cacd8a20a differ diff --git a/temp/85751a2b6d84c3fdf149aaf612caa36d b/temp/85751a2b6d84c3fdf149aaf612caa36d new file mode 100644 index 000000000..f76f06c61 Binary files /dev/null and b/temp/85751a2b6d84c3fdf149aaf612caa36d differ diff --git a/temp/870fbcc3388a7ff2048c10acfba04a19 b/temp/870fbcc3388a7ff2048c10acfba04a19 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/870fbcc3388a7ff2048c10acfba04a19 differ diff --git a/temp/890d823ff5bb652fc5a52ef4b03806af b/temp/890d823ff5bb652fc5a52ef4b03806af new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/890d823ff5bb652fc5a52ef4b03806af differ diff --git a/temp/8deb4b5c80025bf4da947ff2beda9077 b/temp/8deb4b5c80025bf4da947ff2beda9077 new file mode 100644 index 000000000..fc714d3f0 Binary files /dev/null and b/temp/8deb4b5c80025bf4da947ff2beda9077 differ diff --git a/temp/92690a38ca07d22215a28e359833072b b/temp/92690a38ca07d22215a28e359833072b new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/92690a38ca07d22215a28e359833072b differ diff --git a/temp/966eede2f53415b27326528060957e57 b/temp/966eede2f53415b27326528060957e57 new file mode 100644 index 000000000..7e9e4c07c Binary files /dev/null and b/temp/966eede2f53415b27326528060957e57 differ diff --git a/temp/9a6d2562307e8b4e4cde269451828d5b b/temp/9a6d2562307e8b4e4cde269451828d5b new file mode 100644 index 000000000..77a1b03ba Binary files /dev/null and b/temp/9a6d2562307e8b4e4cde269451828d5b differ diff --git a/temp/9b3206dbda9980ae61fe6c386d490435 b/temp/9b3206dbda9980ae61fe6c386d490435 new file mode 100644 index 000000000..2614cb1b9 Binary files /dev/null and b/temp/9b3206dbda9980ae61fe6c386d490435 differ diff --git a/temp/aeade93ca761857beff0d25815721880 b/temp/aeade93ca761857beff0d25815721880 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/aeade93ca761857beff0d25815721880 differ diff --git a/temp/b355b71a980a0067922bd29d2053e1c6 b/temp/b355b71a980a0067922bd29d2053e1c6 new file mode 100644 index 000000000..fc714d3f0 Binary files /dev/null and b/temp/b355b71a980a0067922bd29d2053e1c6 differ diff --git a/temp/b65fa4897c05a3873af427c636f5b05f b/temp/b65fa4897c05a3873af427c636f5b05f new file mode 100644 index 000000000..86d764a71 Binary files /dev/null and b/temp/b65fa4897c05a3873af427c636f5b05f differ diff --git a/temp/d1988277ecd75c7142929494ecd7912d b/temp/d1988277ecd75c7142929494ecd7912d new file mode 100644 index 000000000..f76f06c61 Binary files /dev/null and b/temp/d1988277ecd75c7142929494ecd7912d differ diff --git a/temp/d873a0552b932c81f0f5886b3a36c468 b/temp/d873a0552b932c81f0f5886b3a36c468 new file mode 100644 index 000000000..f07d87768 Binary files /dev/null and b/temp/d873a0552b932c81f0f5886b3a36c468 differ diff --git a/temp/dbaa924008112d8975614432bcf391aa b/temp/dbaa924008112d8975614432bcf391aa new file mode 100644 index 000000000..bf284ee9c Binary files /dev/null and b/temp/dbaa924008112d8975614432bcf391aa differ diff --git a/temp/dc4be4625f303e997344e151a623fcfd b/temp/dc4be4625f303e997344e151a623fcfd new file mode 100644 index 000000000..92201f058 Binary files /dev/null and b/temp/dc4be4625f303e997344e151a623fcfd differ diff --git a/temp/e743756db6a60e10a2feae1c6a765e94 b/temp/e743756db6a60e10a2feae1c6a765e94 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/e743756db6a60e10a2feae1c6a765e94 differ diff --git a/temp/e7e36bc7f0a2c1ef6466284e09fbccb1 b/temp/e7e36bc7f0a2c1ef6466284e09fbccb1 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/e7e36bc7f0a2c1ef6466284e09fbccb1 differ diff --git a/temp/eb58631964af1e02dab7b4a583cbb0dc b/temp/eb58631964af1e02dab7b4a583cbb0dc new file mode 100644 index 000000000..fc714d3f0 Binary files /dev/null and b/temp/eb58631964af1e02dab7b4a583cbb0dc differ diff --git a/temp/f9bcc9fcf4c92b8799b20eae56113129 b/temp/f9bcc9fcf4c92b8799b20eae56113129 new file mode 100644 index 000000000..fc714d3f0 Binary files /dev/null and b/temp/f9bcc9fcf4c92b8799b20eae56113129 differ diff --git a/temp/fae2b29735a04a666a311819970f95d9 b/temp/fae2b29735a04a666a311819970f95d9 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/fae2b29735a04a666a311819970f95d9 differ diff --git a/temp/ffce651681d1f82fc5c5925e4aa2b5e6 b/temp/ffce651681d1f82fc5c5925e4aa2b5e6 new file mode 100644 index 000000000..c398a7375 Binary files /dev/null and b/temp/ffce651681d1f82fc5c5925e4aa2b5e6 differ diff --git a/views/admin/signin.hbs b/views/admin/signin.hbs new file mode 100644 index 000000000..e927c0d90 --- /dev/null +++ b/views/admin/signin.hbs @@ -0,0 +1,37 @@ + + +
+ +

+ 後台登入 +

+
+
+ {{> signin-tab}} +
+
+ +
+
+
\ No newline at end of file diff --git a/views/admin/tweets.hbs b/views/admin/tweets.hbs new file mode 100644 index 000000000..04778fce7 --- /dev/null +++ b/views/admin/tweets.hbs @@ -0,0 +1,53 @@ +
+ {{> admin-sidebar route='tweets'}} +
+
+
+

推文清單

+
+
+
+ {{#each tweets}} +
+ +
+ +
+ +
+ +
+ +
+
+ {{!-- --}} +
+
+
+ + +
+

{{this.description}}

+
+
+ +
+ {{/each}} +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/views/admin/users.hbs b/views/admin/users.hbs new file mode 100644 index 000000000..a713ce756 --- /dev/null +++ b/views/admin/users.hbs @@ -0,0 +1,143 @@ + + +
+ {{> admin-sidebar route='users'}} +
+ +
+

使用者列表

+
+
+ {{#each users}} +
+ +
+ +
+ +
+
+ +
+
+

{{this.name}}

+ +
+
+ +
+
+
+ +

{{this.tweetCount}}

+
+ +
+
+

{{this.followingCount}}個跟隨中

+

{{this.followerCount}}位跟隨者

+
+
+ +
+ {{/each}} +
+
+
\ No newline at end of file diff --git a/views/layouts/main.hbs b/views/layouts/main.hbs new file mode 100644 index 000000000..76fe51d3b --- /dev/null +++ b/views/layouts/main.hbs @@ -0,0 +1,25 @@ + + + + + + + Simple Twitter + + + + {{>messages}} + {{{body}}} + + + + \ No newline at end of file diff --git a/views/partials/admin-sidebar.hbs b/views/partials/admin-sidebar.hbs new file mode 100644 index 000000000..1bdc1c2f1 --- /dev/null +++ b/views/partials/admin-sidebar.hbs @@ -0,0 +1,36 @@ +
+
+ logo-SVG +
+ +
+ + logout + +
+
\ No newline at end of file diff --git a/views/partials/edit-modal.hbs b/views/partials/edit-modal.hbs new file mode 100644 index 000000000..7addba84b --- /dev/null +++ b/views/partials/edit-modal.hbs @@ -0,0 +1,69 @@ + + + diff --git a/views/partials/follower.hbs b/views/partials/follower.hbs new file mode 100644 index 000000000..4c5e460f0 --- /dev/null +++ b/views/partials/follower.hbs @@ -0,0 +1,35 @@ +
+ {{#each follows}} +
+
+ + + +
+
+
+ + {{#if isnotUser}} + {{#if this.isFollowed }} +
+ +
+ {{else}} +
+ +
+ {{/if}} + {{/if}} +
+
+ {{this.introduction}} +
+
+
+ {{/each}} +
\ No newline at end of file diff --git a/views/partials/following.hbs b/views/partials/following.hbs new file mode 100644 index 000000000..d2917f6f3 --- /dev/null +++ b/views/partials/following.hbs @@ -0,0 +1,37 @@ +
+ {{#each follows}} +
+
+ + + +
+
+
+ + {{#if isnotUser}} + {{#if this.isFollowed }} +
+ +
+ {{else}} +
+ +
+ {{!--
--}} + {{/if}} + {{/if}} + +
+
+ {{this.introduction}} +
+
+
+ {{/each}} + \ No newline at end of file diff --git a/views/partials/form-msg.hbs b/views/partials/form-msg.hbs new file mode 100644 index 000000000..43a30dc43 --- /dev/null +++ b/views/partials/form-msg.hbs @@ -0,0 +1,13 @@ + + +{{#if account_messages}} +

+ {{ account_messages }} +

+{{/if}} \ No newline at end of file diff --git a/views/partials/messages.hbs b/views/partials/messages.hbs new file mode 100644 index 000000000..0c488bf49 --- /dev/null +++ b/views/partials/messages.hbs @@ -0,0 +1,56 @@ +
+ + {{#if success_messages}} + + + {{/if}} + + {{#if error_messages}} + + {{/if}} + + {{#if warning_messages}} + + {{/if}} + + {{#if info_messages}} + + {{/if}} + + {{#if errors}} + {{#each errors}} +
+

{{this}}

+ + +
+ {{/each}} + {{/if}} +
\ No newline at end of file diff --git a/views/partials/profile-card.hbs b/views/partials/profile-card.hbs new file mode 100644 index 000000000..d154d6961 --- /dev/null +++ b/views/partials/profile-card.hbs @@ -0,0 +1,99 @@ + +
+ + + + + {{user.name}} + {{user.Tweets.length}}推文 + +
+ + +
+
+ ... + user_Aavtar +
+ +
+ {{#if isUser}} + + {{else}} +
+ + + +
+ {{#if this.isFollowed}} +
+ +
+ {{else}} +
+ +
+ {{/if}} + {{#if this.isFollowed}} +
+
+ +
+
+ {{else}} +
+
+ + +
+ +
+ {{/if}} + {{/if}} +
+
+ + +
+
+

{{user.name}}

+ {{user.account}} +

+ {{#if user.introduction}}{{user.introduction}}{{else}}{{user.name}}尚無自我介紹{{/if}}

+ + {{user.Followings.length}}個跟隨中 + + + {{user.Followers.length}}位跟隨者 + +
+
+ + +
+ +
\ No newline at end of file diff --git a/views/partials/recommendation.hbs b/views/partials/recommendation.hbs new file mode 100644 index 000000000..74c5db92d --- /dev/null +++ b/views/partials/recommendation.hbs @@ -0,0 +1,63 @@ +
+
+
+

推薦跟隨

+
+
+ {{#each recommend}} +
+
+ + + +
+
+ +

{{this.name}}

+
+

@ {{this.account}}

+
+ +
+ {{/each}} +
+
+
\ No newline at end of file diff --git a/views/partials/reply-li.hbs b/views/partials/reply-li.hbs new file mode 100644 index 000000000..c003ae218 --- /dev/null +++ b/views/partials/reply-li.hbs @@ -0,0 +1,35 @@ +
+
+ + + +
+
+ +
+ {{this.comment}} +
+
+
\ No newline at end of file diff --git a/views/partials/sidebar-left.hbs b/views/partials/sidebar-left.hbs new file mode 100644 index 000000000..ab659d02d --- /dev/null +++ b/views/partials/sidebar-left.hbs @@ -0,0 +1,80 @@ +
+
+ logo-SVG +
+ +
+ + logout + +
+
+ +{{!-- modal --}} + \ No newline at end of file diff --git a/views/partials/signin-tab.hbs b/views/partials/signin-tab.hbs new file mode 100644 index 000000000..4158bac33 --- /dev/null +++ b/views/partials/signin-tab.hbs @@ -0,0 +1,52 @@ + + +
+ + + {{> form-msg}} +
+
+ + +
+
+ +
\ No newline at end of file diff --git a/views/partials/tweet-li.hbs b/views/partials/tweet-li.hbs new file mode 100644 index 000000000..8a8319884 --- /dev/null +++ b/views/partials/tweet-li.hbs @@ -0,0 +1,186 @@ +
+
+ + + +
+
+ + +
+
+ +

{{this.replies}}

+
+ +
+
+
+ +{{! modal }} + \ No newline at end of file diff --git a/views/partials/user-form.hbs b/views/partials/user-form.hbs new file mode 100644 index 000000000..b3097d2ee --- /dev/null +++ b/views/partials/user-form.hbs @@ -0,0 +1,66 @@ + + +
+ + +

{{accountMsg}}

+
+
+ + +

{{nameMsg}}

+
+
+ + +

{{mailMsg}}

+
+
+ + +

{{passwordMsg}}

+
+
+ + +

{{passwordMsg}}

+
\ No newline at end of file diff --git a/views/settings.hbs b/views/settings.hbs new file mode 100644 index 000000000..7259e1c18 --- /dev/null +++ b/views/settings.hbs @@ -0,0 +1,72 @@ +
+ {{>sidebar-left route="settings"}} +
+
+
+
+

帳戶設定

+
+ +
+
+ + {{> user-form}} + + {{!--
+ + +

{{accountMsg}}

+
+ +
+ + +
+ +
+ + +

{{mailMsg}}

+
+ +
+ + +

{{passwordMsg}}

+
+ +
+ + +

{{passwordMsg}}

+
--}} + +
+ +
+ +
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/views/signin.hbs b/views/signin.hbs new file mode 100644 index 000000000..6ef6a3ed3 --- /dev/null +++ b/views/signin.hbs @@ -0,0 +1,43 @@ + + +
+ +

+ 登入 Alphitter +

+
+
+ {{> signin-tab}} +
+
+ +
+
+
\ No newline at end of file diff --git a/views/signup.hbs b/views/signup.hbs new file mode 100644 index 000000000..1c6f70a26 --- /dev/null +++ b/views/signup.hbs @@ -0,0 +1,71 @@ + + +
+
+ +
+

+ 建立你的帳號 +

+
+
+ {{> user-form}} +
+ +
+
+
+ +
+
+
\ No newline at end of file diff --git a/views/tweet.hbs b/views/tweet.hbs new file mode 100644 index 000000000..142d07ae5 --- /dev/null +++ b/views/tweet.hbs @@ -0,0 +1,137 @@ +
+ {{>sidebar-left}} +
+
+ +

推文

+
+
+
+
+ + + +
+ +
+
+ {{tweet.description}} +
+

{{hourTime tweet.createdAt}}.{{dateTime tweet.createdAt}}

+
+
+
+

{{repliesAmount}}

+

回覆

+
+ +
+
+
+
+ +
+ +
+
+
+ {{#each tweet.Replies}} + {{>reply-li}} + {{/each}} +
+
+ {{>recommendation}} +
+ +{{!-- modal --}} + + \ No newline at end of file diff --git a/views/tweets.hbs b/views/tweets.hbs new file mode 100644 index 000000000..58bcd4bf5 --- /dev/null +++ b/views/tweets.hbs @@ -0,0 +1,42 @@ +
+ {{> sidebar-left route="home"}} +
+
+
+

首頁

+
+
+
+
+ +
+ +
+ {{#if error_messages}} +

{{error_messages}}

+ {{/if}} + +
+
+
+
+
+
+ {{#each tweets}} + {{> tweet-li}} + {{/each}} + + {{#if moreTweets}} + {{#each moreTweets}} + {{> tweet-li}} + {{/each}} + {{/if}} +
+
+
+
+ {{> recommendation}} +
diff --git a/views/user/user-follower.hbs b/views/user/user-follower.hbs new file mode 100644 index 000000000..d0dd42d4a --- /dev/null +++ b/views/user/user-follower.hbs @@ -0,0 +1,39 @@ +
+ {{> sidebar-left route="profile"}} +
+ + +
+ {{> recommendation}} +
+ diff --git a/views/user/user-following.hbs b/views/user/user-following.hbs new file mode 100644 index 000000000..3769c2177 --- /dev/null +++ b/views/user/user-following.hbs @@ -0,0 +1,37 @@ +
+ {{> sidebar-left route="profile"}} +
+ + +
+ {{> recommendation}} +
diff --git a/views/user/user-likes.hbs b/views/user/user-likes.hbs new file mode 100644 index 000000000..2593d1a71 --- /dev/null +++ b/views/user/user-likes.hbs @@ -0,0 +1,21 @@ +
+ {{> sidebar-left route="profile"}} +
+ {{>profile-card route='user-likes'}} + +
+ {{> recommendation}} +
+{{> edit-modal}} diff --git a/views/user/user-replies.hbs b/views/user/user-replies.hbs new file mode 100644 index 000000000..595bb198d --- /dev/null +++ b/views/user/user-replies.hbs @@ -0,0 +1,45 @@ +
+ {{> sidebar-left route="profile"}} +
+ {{>profile-card route='user-replies'}} +
+
+ {{#if replies}} + {{#each replies}} +
+
+ + avatar + +
+
+ + +
+
+ {{/each}} + {{else}} +
+ 尚無回覆 +
+ {{/if}} +
+
+
+ {{> recommendation}} +
+{{> edit-modal}} diff --git a/views/user/user-tweets.hbs b/views/user/user-tweets.hbs new file mode 100644 index 000000000..b398f42c9 --- /dev/null +++ b/views/user/user-tweets.hbs @@ -0,0 +1,21 @@ +
+ {{> sidebar-left route="profile"}} +
+ {{>profile-card route='user-tweets'}} +
+
+ {{#if tweets}} + {{#each tweets}} + {{> tweet-li}} + {{/each}} + {{else}} +
+ 尚無推文 +
+ {{/if}} +
+
+
+ {{> recommendation}} +
+{{> edit-modal}} \ No newline at end of file