├── .gitignore ├── README.md ├── assets └── SingleLink-Brandmark.svg ├── functions ├── Analytics │ ├── fetch.js │ ├── index.js │ └── link.js ├── Link │ ├── create.js │ ├── destroy.js │ ├── index.js │ ├── reorder.js │ ├── reset-order.js │ └── update.js ├── Misc │ └── status.js ├── Profile │ ├── activate-theme.js │ ├── create.js │ ├── destroy.js │ ├── fetch-preview.js │ ├── fetch.js │ ├── index.js │ ├── links.js │ ├── list.js │ ├── thumbnail.js │ └── update.js ├── Theme │ ├── create.js │ ├── fetch.js │ └── index.js └── User │ ├── create.js │ ├── fetch.js │ ├── index.js │ ├── login.js │ ├── set-active.js │ └── update.js ├── index.js ├── middleware └── auth.js ├── models ├── Link.js ├── Profile.js ├── Theme.js ├── User.js └── Visit.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .vim 2 | .idea 3 | .eclipse 4 | .atom 5 | .vscode 6 | node_modules 7 | config.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

SingleLink Logo

2 |

3 | Version 4 | License 5 | Build Status 6 | Users 7 |

8 |

Welcome to the SingleLink API Github repository. Here you can browse the source, view open issues and monitor development.

-------------------------------------------------------------------------------- /assets/SingleLink-Brandmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /functions/Analytics/fetch.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require('bcrypt-nodejs'); 2 | const jwt = require('jsonwebtoken'); 3 | const mongoose = require('mongoose'); 4 | 5 | const User = mongoose.model('User'); 6 | const Profile = mongoose.model('Profile'); 7 | const Link = mongoose.model('Link'); 8 | const Theme = mongoose.model('Theme'); 9 | 10 | module.exports = async (req, res) => { 11 | // Fetch items from database 12 | let users = await User.find(); 13 | let profiles = await Profile.find(); 14 | let links = await Link.find(); 15 | let themes = await Theme.find(); 16 | 17 | // Compute additional values from items 18 | // Filter published profiles from unpublished profiles 19 | let profiles_published = 0; 20 | for(let i=0;i { 6 | let link, visit; 7 | try { 8 | link = await Link.findOne({_id: req.params._id }); 9 | visit = new Visit({ 10 | type: 'Link', 11 | referral: req.params._id 12 | }); 13 | visit = await visit.save(); 14 | } catch(err) { 15 | return res.send(err.message); 16 | } finally { 17 | return res.redirect(link.url); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /functions/Link/create.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Link = mongoose.model('Link'); 4 | 5 | module.exports = (req, res) => { 6 | if(!req.user.active_profile) return res.status(400).send('You need an active profile to do this'); 7 | if(!req.body.label) return res.status(400).send('Link label required'); 8 | if(!req.body.url) return res.status(400).send('Link URL required'); 9 | Link.countDocuments({parent: req.user.active_profile._id}, (err, count) => { 10 | if(err) return res.send(err); 11 | if(!count) count = 0; 12 | new Link({ 13 | label: req.body.label, 14 | subtitle: req.body.subtitle || null, 15 | url: req.body.url, 16 | custom_css: req.body.custom_css || null, 17 | parent: req.user.active_profile._id, 18 | order: count 19 | }).save((err, link) => { 20 | if(err) return res.send(err); 21 | return res.send(link); 22 | }); 23 | }); 24 | } -------------------------------------------------------------------------------- /functions/Link/destroy.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Link = mongoose.model('Link'); 4 | 5 | module.exports = async (req, res) => { 6 | if(!req.body.target) return res.status(400).send('Target required'); 7 | Link.findOneAndDelete({ 8 | _id: req.body.target 9 | }, async (err) => { 10 | if(err) return res.send(err); 11 | let links = await Link.find({parent:req.user.active_profile._id}).sort({"order":1});; 12 | for(let i=0;i { 6 | if(!req.user.active_profile) return res.status(400).send('You need an active profile to do this'); 7 | if(!req.body.target) return res.status(400).send('Target required'); 8 | if(req.body.new_index === null) return res.status(400).send('New index required'); 9 | if(req.body.old_index === null) return res.status(400).send('Old index required'); 10 | 11 | let links = await Link.find({parent: req.user.active_profile._id}).sort({"order":1}); 12 | 13 | console.log('Before'); 14 | console.log(links); 15 | 16 | let end_links = links.splice(req.body.new_index); 17 | if(req.body.old_index > req.body.new_index) { 18 | // Search end_links 19 | end_links.splice(req.body.old_index - req.body.new_index, 1); 20 | } else { 21 | // Search links 22 | links.splice(req.body.old_index, 1); 23 | } 24 | 25 | let target = await Link.findOne({_id:req.body.target}); 26 | 27 | links = links.concat(end_links); 28 | 29 | links.splice(req.body.new_index, 0,target); 30 | 31 | for(let i=0;i { 6 | if(!req.body.target) return res.status(400).send('Target required'); 7 | Link.findOne({ 8 | _id: req.body.target 9 | }, (err, link) => { 10 | if(err) return res.send(err); 11 | if(req.body.label) link.label = req.body.label; 12 | link.subtitle = req.body.subtitle || null; 13 | if(req.body.url) link.url = req.body.url; 14 | link.custom_css = req.body.custom_css || null; 15 | link.save((err, link) => { 16 | if(err) return res.send(err); 17 | Link.find({ 18 | parent: req.user.active_profile 19 | }, (err, links) => { 20 | if(err) return res.send(err); 21 | res.send(links); 22 | }); 23 | }) 24 | }) 25 | } -------------------------------------------------------------------------------- /functions/Misc/status.js: -------------------------------------------------------------------------------- 1 | module.exports = (req, res) => { 2 | res.send( 3 | '\n' + 4 | ' \n' + 5 | ' \n' + 6 | ' \n' + 7 | ' Status Page | Singlelink\n' + 8 | ' \n' + 9 | ' \n' + 10 | '
\n' + 11 | '

Status Page

\n' + 12 | '

Welcome to the Singlelink status page, view the status and downtime of various application infrastructure below.

\n' + 13 | '
\n' + 14 | '
' + 15 | '

Singlelink API

' + 16 | '

99.9% Uptime

' + 17 | '
\n' + 18 | '
\n' + 19 | '
' + 20 | '

Singlelink Client

' + 21 | '

99.9% Uptime

' + 22 | '
\n' + 23 | '
\n' + 24 | '
' + 25 | '

Singlelink Database

' + 26 | '

99.9% Uptime

' + 27 | '
\n' + 28 | '
\n' + 29 | '
' + 30 | '

Community Support

' + 31 | '

100% Uptime

' + 32 | '
\n' + 33 | '
\n' + 34 | '
All rights reserved.
Copyright ©2020 Neutron Creative Inc.
\n' + 35 | ' \n' + 36 | '\n' 37 | ); 38 | }; 39 | /* 40 | 41 | 42 | 43 | 44 | 45 |
46 |

Singlelink API

47 |
48 | 49 | 50 | */ -------------------------------------------------------------------------------- /functions/Profile/activate-theme.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Profile = mongoose.model('Profile'); 4 | 5 | module.exports = (req, res) => { 6 | req.user.active_profile.theme = req.body.theme || null; 7 | req.user.active_profile.save((err, profile) => { 8 | if(err) return res.send(err); 9 | return res.send(profile); 10 | }); 11 | } -------------------------------------------------------------------------------- /functions/Profile/create.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | const Profile = mongoose.model('Profile'); 4 | 5 | module.exports = async function(req, res) { 6 | new Profile({ 7 | handle: req.body.handle || Math.random().toString(36).substring(2, 6) + Math.random().toString(36).substring(2, 6), 8 | parent: req.user._id, 9 | image_url: req.body.image_url || null, 10 | headline: req.body.headline || null, 11 | subtitle: req.body.subtitle || null 12 | }).save((err, profile) => { 13 | if(err) return res.send(err); 14 | if(!profile) return res.status(400).send('Failed to create profile'); 15 | req.user.active_profile = profile._id; 16 | req.user.save((err, user) => { 17 | return res.send(profile); 18 | }); 19 | }); 20 | } -------------------------------------------------------------------------------- /functions/Profile/destroy.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Profile = mongoose.model('Profile'); 4 | 5 | module.exports = async (req, res) => { 6 | if(!req.user.active_profile) return res.status(400).send('Active profile required'); 7 | const user_profiles = await Profile.find({parent: req.user._id}); 8 | if(user_profiles.length <= 1) return res.status(400).send('You need to make another profile before deleting this one'); 9 | await Profile.deleteOne({_id: req.user.active_profile}); 10 | const remaining_profiles = await Profile.find({parent: req.user._id}); 11 | req.user.active_profile = remaining_profiles[0]; 12 | await req.user.save(); 13 | return res.send(remaining_profiles[0]); 14 | } -------------------------------------------------------------------------------- /functions/Profile/fetch-preview.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Profile = mongoose.model('Profile'); 4 | var Link = mongoose.model('Link'); 5 | var User = mongoose.model('User'); 6 | var Theme = mongoose.model('Theme'); 7 | 8 | module.exports = async (req, res) => { 9 | var payload = { 10 | profile: req.user.active_profile, 11 | profiles: await Profile.find({parent: req.user._id}), 12 | links: null, 13 | user: req.user, 14 | theme: null 15 | }; 16 | Link.find({ 17 | parent: req.user.active_profile._id 18 | }, async (err, links) => { 19 | if(payload.profile.theme) payload.theme = await Theme.findOne({_id: payload.profile.theme || ''}); 20 | if(err) return res.send(err); 21 | payload.links = links || []; 22 | res.send(payload); 23 | }); 24 | } -------------------------------------------------------------------------------- /functions/Profile/fetch.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Profile = mongoose.model('Profile'); 4 | const Visit = mongoose.model('Visit'); 5 | const Link = mongoose.model('Link'); 6 | const User = mongoose.model('User'); 7 | const Theme = mongoose.model('Theme'); 8 | 9 | module.exports = (req, res) => { 10 | if(!req.body.handle && !req.params.handle) return res.status(400).send('Handle required to find account'); 11 | let payload = { 12 | profile: null, 13 | links: null, 14 | user: null, 15 | theme: null 16 | }; 17 | Profile.findOne({ 18 | handle: req.body.handle || req.params.handle 19 | }, async (err, profile) => { 20 | if(err) return res.send(err); 21 | if(!profile) return res.status(404).send('Profile not found'); 22 | if(profile.visibility == 'unpublished') return res.status(404).send('Profile not found'); 23 | payload.profile = profile; 24 | if(profile.theme) payload.theme = await Theme.findOne({_id: profile.theme || ''}); 25 | User.findOne({ 26 | _id: profile.parent 27 | }, (err, user) => { 28 | if(err) return res.send(err); 29 | if(!user) return res.status(404).send('Profile parent not found'); 30 | payload.user = user; 31 | Link.find({ 32 | parent: profile._id 33 | }, async (err, links) => { 34 | if(err) return res.send(err); 35 | payload.links = links || []; 36 | let visit = await new Visit({ 37 | type: 'Page', 38 | referral: profile._id 39 | }).save(); 40 | res.send(payload); 41 | }); 42 | }) 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /functions/Profile/index.js: -------------------------------------------------------------------------------- 1 | // Third-party assets & dependancies 2 | var express = require('express'); 3 | var mongoose = require('mongoose'); 4 | 5 | var ProfileController = express(); 6 | 7 | // Routing 8 | //ProfileController.all('/', require('./')); 9 | 10 | ProfileController.post('/fetch', require('./fetch')); 11 | ProfileController.all('/fetch/:handle', require('./fetch')); 12 | 13 | // Thumbnail routing 14 | ProfileController.all('/thumbnail/:handle', require('./thumbnail')); 15 | 16 | // From here on out, require authentication 17 | ProfileController.use(require('../../middleware/auth')); 18 | 19 | ProfileController.post('/create', require('./create')); 20 | 21 | ProfileController.post('/fetch-preview', require('./fetch-preview')); 22 | 23 | ProfileController.post('/activate-theme', require('./activate-theme')); 24 | 25 | ProfileController.post('/update', require('./update')); 26 | 27 | ProfileController.post('/links', require('./links')); 28 | 29 | ProfileController.post('/list', require('./list')); 30 | 31 | ProfileController.post('/destroy', require('./destroy')); 32 | 33 | module.exports = ProfileController; 34 | -------------------------------------------------------------------------------- /functions/Profile/links.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Profile = mongoose.model('Profile'); 4 | var Link = mongoose.model('Link'); 5 | 6 | module.exports = (req, res) => { 7 | if(!req.user.active_profile) return res.status(400).send('You need an active profile to do this'); 8 | Link.find({parent:req.user.active_profile._id}) 9 | .exec((err, links) => { 10 | if(err) return res.send(err); 11 | return res.send(links); 12 | }); 13 | } -------------------------------------------------------------------------------- /functions/Profile/list.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Profile = mongoose.model('Profile'); 4 | 5 | module.exports = (req, res) => { 6 | Profile.find({parent:req.user._id}) 7 | .exec((err, profiles) => { 8 | if(err) return res.send(err); 9 | return res.send(profiles); 10 | }); 11 | } -------------------------------------------------------------------------------- /functions/Profile/thumbnail.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios'); 2 | 3 | // All good 😎 4 | 5 | module.exports = async (req, res) => { 6 | if(!req.params.handle) return res.status(400).send('Handle required to make request'); 7 | let url, thumbnail; 8 | let final_size = { 9 | x: 1200, 10 | y: 630 11 | }; 12 | let scale = 3; 13 | let resolution = { 14 | x: final_size.x/scale, 15 | y: final_size.y/scale 16 | } 17 | try { 18 | url = 'https://capture.neutroncreative.com/api/v1/capture?apiKey=' + global.config.capture_key + '&url=https://app.singlelink.co/u/' + req.params.handle + '&size=' + resolution.x + 'x' + resolution.y + '&crop=true&scale=' + scale; 19 | thumbnail = await axios.get(url, { 20 | responseType: "arraybuffer" 21 | }) 22 | } catch (err) { 23 | return res.send(err.message + '
' + url || 'Error!'); 24 | } finally { 25 | //return res.send(btoa(thumbnail.data)); 26 | 27 | let thumbnail_img = "data:" + thumbnail.headers["content-type"] + ";base64," + Buffer.from(thumbnail.data).toString('base64') 28 | 29 | res.writeHead(200, { 30 | 'Content-Type': 'image/png', 31 | 'Content-Length': thumbnail.data.length 32 | }); 33 | return res.end(thumbnail.data); 34 | return res.sendFile(thumbnail.data); 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /functions/Profile/update.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Profile = mongoose.model('Profile'); 4 | 5 | module.exports = (req, res) => { 6 | req.user.active_profile.image_url = req.body.image_url || null; 7 | if(req.body.headline) req.user.active_profile.headline = req.body.headline; 8 | if(req.body.subtitle) req.user.active_profile.subtitle = req.body.subtitle; 9 | if(req.body.handle) req.user.active_profile.handle = req.body.handle; 10 | if(req.body.visibility) req.user.active_profile.visibility = req.body.visibility; 11 | if(typeof req.body.custom_css != 'undefined') req.user.active_profile.custom_css = req.body.custom_css || ''; 12 | if(typeof req.body.custom_html != 'undefined') req.user.active_profile.custom_html = req.body.custom_html || ''; 13 | 14 | req.user.active_profile.save((err, profile) => { 15 | if(err) return res.send(err); 16 | res.send(profile); 17 | }) 18 | } -------------------------------------------------------------------------------- /functions/Theme/create.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var Theme = mongoose.model('Theme'); 4 | 5 | module.exports = (req, res) => { 6 | if(!req.body.label) return res.status(400).send('Theme label required'); 7 | new Theme({ 8 | label: req.body.label, 9 | colors: req.body.colors || null, 10 | custom_css: req.body.custom_css || null, 11 | custom_html: req.body.custom_html || null, 12 | parent: req.user._id 13 | }).save((err, link) => { 14 | if(err) return res.send(err); 15 | return res.send(link); 16 | }); 17 | } -------------------------------------------------------------------------------- /functions/Theme/fetch.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Theme = mongoose.model('Theme'); 4 | 5 | module.exports = (req, res) => { 6 | let payload = []; 7 | Theme.find({ 8 | parent: req.user._id 9 | }) 10 | .exec((err, themes) => { 11 | if(err) return res.send(err); 12 | payload = payload.concat(themes); 13 | Theme.find({ 14 | global: true, 15 | }) 16 | .exec((err, themes) => { 17 | if(err) return res.send(err); 18 | for(let i=0;i { 10 | if(!req.body.email) return res.status(400).send('Missing email'); 11 | if(!req.body.password) return res.status(400).send('Missing password'); 12 | User.findOne({ email : req.body.email }, function(err, user) { 13 | if (err) return res.status(500).send(err); 14 | if (user) return res.status(404).send('User with that email address already exists'); 15 | bcrypt.hash(req.body.password, null, null, function (err, hash) { 16 | if (err) return res.status(500).send(err); 17 | new User({ 18 | name: req.body.name || null, 19 | email: req.body.email || null, 20 | password: hash, 21 | hash: md5(req.body.email) 22 | }).save(function (err, user) { 23 | 24 | new Profile({parent: user._id, handle: req.body.handle || user._id }).save(function(err, profile) { 25 | user.active_profile = profile._id; 26 | user.save(function (err, user) { 27 | if (err) return res.send(err); 28 | return res.status(201).json({ 29 | user: user, 30 | active_profile: profile, 31 | token: jwt.sign( 32 | { 33 | email: user.email, 34 | }, global.config.secret, { 35 | expiresIn: '168h' 36 | } 37 | ) 38 | }); 39 | }); 40 | }); 41 | }); 42 | }); 43 | }); 44 | } -------------------------------------------------------------------------------- /functions/User/fetch.js: -------------------------------------------------------------------------------- 1 | module.exports = function(req, res) { 2 | res.send(req.user); 3 | } -------------------------------------------------------------------------------- /functions/User/index.js: -------------------------------------------------------------------------------- 1 | // Third-party assets & dependancies 2 | var express = require('express'); 3 | var mongoose = require('mongoose'); 4 | 5 | var UserController = express(); 6 | 7 | // Routing 8 | UserController.all('/login', require('./login')); 9 | UserController.all('/create', require('./create')); 10 | 11 | // From here on out, require authentication 12 | UserController.use(require('../../middleware/auth')); 13 | 14 | UserController.all('/fetch', require('./fetch')); 15 | UserController.all('/set-active', require('./set-active')); 16 | 17 | module.exports = UserController; 18 | -------------------------------------------------------------------------------- /functions/User/login.js: -------------------------------------------------------------------------------- 1 | var bcrypt = require('bcrypt-nodejs'); 2 | var jwt = require('jsonwebtoken'); 3 | var mongoose = require('mongoose'); 4 | 5 | var User = mongoose.model('User'); 6 | 7 | module.exports = (req, res) => { 8 | if(!req.body.email) return res.status(400).send('Missing email'); 9 | if(!req.body.password) return res.status(400).send('Missing password'); 10 | User.findOne({ email : req.body.email }).populate('active_profile').exec(function(err, user) { 11 | if (err) return res.status(500).send(err); 12 | if (!user) return res.status(404).send('User with email address cannot be found'); 13 | bcrypt.compare(req.body.password, user.password, function (err, verified) { 14 | if(err) return res.status(500).send(err); 15 | if(!verified) return res.status(401).send('Incorrect password'); 16 | return res.json({ 17 | user: { 18 | _id: user._id, 19 | email: user.email, 20 | }, 21 | active_profile: user.active_profile, 22 | token: jwt.sign( 23 | { 24 | email: user.email, 25 | }, global.config.secret, { 26 | expiresIn: '168h' 27 | } 28 | ) 29 | }); 30 | }); 31 | }); 32 | } -------------------------------------------------------------------------------- /functions/User/set-active.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const User = mongoose.model('User'); 4 | const Profile = mongoose.model('Profile'); 5 | 6 | module.exports = async function (req, res) { 7 | if(!req.body.profile) return res.status(400).send('Missing profile'); 8 | let pending_profile = await Profile.findOne({_id: req.body.profile}); 9 | 10 | if(!pending_profile) return res.status(400).send('Could not find profile with provided ID'); 11 | 12 | if(!pending_profile.parent.equals(req.user._id)) return res.status(400).send('User lacks permissions for provided profile'); 13 | 14 | req.user.active_profile = pending_profile._id; 15 | 16 | await req.user.save(); 17 | 18 | return res.send(pending_profile); 19 | } -------------------------------------------------------------------------------- /functions/User/update.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/singlelink-co/Singlelink-API/71f0b014f240c0429935479e4c9493894f301e29/functions/User/update.js -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const getPort = require('get-port'); 3 | const https = require('https'); 4 | const mongoose = require('mongoose'); 5 | const bodyParser = require('body-parser'); 6 | const cors = require('cors'); 7 | 8 | global.config = require('./config'); 9 | 10 | global.mongodb = true; 11 | 12 | mongoose.connect(global.config.database, 13 | { 14 | useNewUrlParser: true 15 | }). 16 | catch(error => function(error) { 17 | global.mongodb = false; 18 | console.log('Error connecting to MongoDB'); 19 | console.log('-------------------------------------'); 20 | console.log(error); 21 | }); 22 | 23 | 24 | const app = express(); 25 | const environment = process.env.NODE_ENV || 'development'; 26 | let port; 27 | 28 | app.use(cors()); 29 | app.use(bodyParser.json()); 30 | app.use(bodyParser.urlencoded({ 31 | extended: true 32 | })); 33 | 34 | port = process.env.API_PORT || config.port || 80; 35 | 36 | app.get('/', require('./functions/Misc/status')); 37 | 38 | const User = require('./models/User'); 39 | const Profile = require('./models/Profile'); 40 | const Link = require('./models/Link'); 41 | const Theme = require('./models/Theme'); 42 | const Visit = require('./models/Visit'); 43 | 44 | app.use('/user', require('./functions/User')); 45 | app.use('/profile', require('./functions/Profile')); 46 | app.use('/link', require('./functions/Link')); 47 | app.use('/theme', require('./functions/Theme')); 48 | app.use('/analytics', require('./functions/Analytics')) 49 | 50 | app.listen(port, () => { 51 | console.log(`🔗 Singlelink API listening on port ${port}`) 52 | }) 53 | -------------------------------------------------------------------------------- /middleware/auth.js: -------------------------------------------------------------------------------- 1 | // functions/middleware/auth.js 2 | 3 | var mongoose = require('mongoose'); 4 | var bcrypt = require('bcrypt-nodejs'); 5 | var jwt = require('jsonwebtoken'); 6 | 7 | var config = global.config; 8 | 9 | const User = mongoose.model('User'); 10 | const Profile = mongoose.model('Profile'); 11 | 12 | module.exports = function(req, res, next) { 13 | var response = res; 14 | if(req.query.token) req.body.token = req.query.token; 15 | if(!req.body.token) return res.status(400).send('Missing token'); 16 | jwt.verify(req.body.token, config.secret, function(err, decoded) { 17 | if(err) return res.status(400).send(err); 18 | if(!decoded.email) return res.status(400).send('Unable to verify user, returning to sign in'); 19 | User.findOne({email: decoded.email}) 20 | .populate('active_profile') 21 | .exec(function(err, user) { 22 | if(err) res.send(err); 23 | req.user = user; 24 | next(); 25 | }); 26 | }) 27 | } -------------------------------------------------------------------------------- /models/Link.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var LinkSchema = new mongoose.Schema({ 4 | label: { 5 | type: String, 6 | required: true 7 | }, 8 | subtitle: String, 9 | url: { 10 | type: String, 11 | default: '#', 12 | required: true 13 | }, 14 | style: String, 15 | parent: { 16 | type: mongoose.Schema.Types.ObjectId, 17 | ref: 'Profile', 18 | required: true 19 | }, 20 | custom_css: String, 21 | order: { 22 | type: Number, 23 | required: true 24 | } 25 | }); 26 | 27 | module.exports = mongoose.model('Link', LinkSchema); -------------------------------------------------------------------------------- /models/Profile.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var ProfileSchema = new mongoose.Schema({ 4 | handle: { 5 | type: String, 6 | unique: true, 7 | required: true 8 | }, 9 | image_url: String, 10 | headline: String, 11 | subtitle: String, 12 | social: [{ 13 | icon: String, 14 | link: String, 15 | alt: String, 16 | }], 17 | parent: { 18 | type: mongoose.Schema.Types.ObjectId, 19 | ref: 'Profile', 20 | required: true 21 | }, 22 | members: [{ 23 | type: mongoose.Schema.Types.ObjectId, 24 | ref: 'User' 25 | }], 26 | visibility: { 27 | type: String, 28 | default: 'unpublished', 29 | required: true, 30 | enum: [ 31 | 'unpublished', 32 | 'published', 33 | 'published-18+' 34 | ] 35 | }, 36 | custom_css: String, 37 | custom_html: String, 38 | theme: { 39 | type: mongoose.Schema.Types.ObjectId, 40 | ref: 'Theme', 41 | }, 42 | }); 43 | 44 | ProfileSchema.virtual('permissions').get(function() { 45 | if(this.members) return this.members.push(this.parent); 46 | return [this.parent]; 47 | }); 48 | 49 | module.exports = mongoose.model('Profile', ProfileSchema); -------------------------------------------------------------------------------- /models/Theme.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var ThemeSchema = new mongoose.Schema({ 4 | label: { 5 | type: String, 6 | required: true 7 | }, 8 | parent: { 9 | type: mongoose.Schema.Types.ObjectId, 10 | ref: 'User', 11 | required: true 12 | }, 13 | global: { 14 | type: Boolean, 15 | default: false, 16 | required: true 17 | }, 18 | colors: { 19 | fill: { 20 | primary: String, 21 | secondary: String 22 | }, 23 | text: { 24 | primary: String, 25 | secondary: String 26 | } 27 | }, 28 | custom_css: String, 29 | custom_html: String 30 | }); 31 | 32 | module.exports = mongoose.model('Theme', ThemeSchema); -------------------------------------------------------------------------------- /models/User.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var UserSchema = new mongoose.Schema({ 4 | name: String, 5 | email: { 6 | type: String, 7 | required: true, 8 | unique: true 9 | }, 10 | password: { 11 | type: String, 12 | }, 13 | hash: { 14 | type: String, 15 | required: true 16 | }, 17 | avatar_url: String, 18 | active_profile: { 19 | type: mongoose.Schema.Types.ObjectId, 20 | ref: 'Profile' 21 | }, 22 | }); 23 | 24 | module.exports = mongoose.model('User', UserSchema); -------------------------------------------------------------------------------- /models/Visit.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const VisitSchema = new mongoose.Schema({ 4 | type: { 5 | type: String, 6 | enum: [ 7 | 'Link', 8 | 'Page' 9 | ] 10 | }, 11 | ip_address: String, 12 | referral: { 13 | type: mongoose.Schema.Types.ObjectId, 14 | }, 15 | }); 16 | 17 | module.exports = mongoose.model('Visit', VisitSchema); 18 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "singlelink", 3 | "version": "0.9.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "axios": { 22 | "version": "0.20.0", 23 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", 24 | "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", 25 | "requires": { 26 | "follow-redirects": "^1.10.0" 27 | } 28 | }, 29 | "bcrypt-nodejs": { 30 | "version": "0.0.3", 31 | "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz", 32 | "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs=" 33 | }, 34 | "bl": { 35 | "version": "2.2.1", 36 | "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", 37 | "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", 38 | "requires": { 39 | "readable-stream": "^2.3.5", 40 | "safe-buffer": "^5.1.1" 41 | } 42 | }, 43 | "bluebird": { 44 | "version": "3.5.1", 45 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 46 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 47 | }, 48 | "body-parser": { 49 | "version": "1.19.0", 50 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 51 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 52 | "requires": { 53 | "bytes": "3.1.0", 54 | "content-type": "~1.0.4", 55 | "debug": "2.6.9", 56 | "depd": "~1.1.2", 57 | "http-errors": "1.7.2", 58 | "iconv-lite": "0.4.24", 59 | "on-finished": "~2.3.0", 60 | "qs": "6.7.0", 61 | "raw-body": "2.4.0", 62 | "type-is": "~1.6.17" 63 | } 64 | }, 65 | "bson": { 66 | "version": "1.1.5", 67 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", 68 | "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" 69 | }, 70 | "buffer-equal-constant-time": { 71 | "version": "1.0.1", 72 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 73 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 74 | }, 75 | "bytes": { 76 | "version": "3.1.0", 77 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 78 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 79 | }, 80 | "charenc": { 81 | "version": "0.0.2", 82 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 83 | "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" 84 | }, 85 | "content-disposition": { 86 | "version": "0.5.3", 87 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 88 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 89 | "requires": { 90 | "safe-buffer": "5.1.2" 91 | } 92 | }, 93 | "content-type": { 94 | "version": "1.0.4", 95 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 96 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 97 | }, 98 | "cookie": { 99 | "version": "0.4.0", 100 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 101 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 102 | }, 103 | "cookie-signature": { 104 | "version": "1.0.6", 105 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 106 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 107 | }, 108 | "core-util-is": { 109 | "version": "1.0.2", 110 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 111 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 112 | }, 113 | "cors": { 114 | "version": "2.8.5", 115 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 116 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 117 | "requires": { 118 | "object-assign": "^4", 119 | "vary": "^1" 120 | } 121 | }, 122 | "crypt": { 123 | "version": "0.0.2", 124 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 125 | "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" 126 | }, 127 | "debug": { 128 | "version": "2.6.9", 129 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 130 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 131 | "requires": { 132 | "ms": "2.0.0" 133 | } 134 | }, 135 | "denque": { 136 | "version": "1.4.1", 137 | "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", 138 | "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" 139 | }, 140 | "depd": { 141 | "version": "1.1.2", 142 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 143 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 144 | }, 145 | "destroy": { 146 | "version": "1.0.4", 147 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 148 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 149 | }, 150 | "ecdsa-sig-formatter": { 151 | "version": "1.0.11", 152 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 153 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 154 | "requires": { 155 | "safe-buffer": "^5.0.1" 156 | } 157 | }, 158 | "ee-first": { 159 | "version": "1.1.1", 160 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 161 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 162 | }, 163 | "encodeurl": { 164 | "version": "1.0.2", 165 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 166 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 167 | }, 168 | "escape-html": { 169 | "version": "1.0.3", 170 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 171 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 172 | }, 173 | "etag": { 174 | "version": "1.8.1", 175 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 176 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 177 | }, 178 | "express": { 179 | "version": "4.17.1", 180 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 181 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 182 | "requires": { 183 | "accepts": "~1.3.7", 184 | "array-flatten": "1.1.1", 185 | "body-parser": "1.19.0", 186 | "content-disposition": "0.5.3", 187 | "content-type": "~1.0.4", 188 | "cookie": "0.4.0", 189 | "cookie-signature": "1.0.6", 190 | "debug": "2.6.9", 191 | "depd": "~1.1.2", 192 | "encodeurl": "~1.0.2", 193 | "escape-html": "~1.0.3", 194 | "etag": "~1.8.1", 195 | "finalhandler": "~1.1.2", 196 | "fresh": "0.5.2", 197 | "merge-descriptors": "1.0.1", 198 | "methods": "~1.1.2", 199 | "on-finished": "~2.3.0", 200 | "parseurl": "~1.3.3", 201 | "path-to-regexp": "0.1.7", 202 | "proxy-addr": "~2.0.5", 203 | "qs": "6.7.0", 204 | "range-parser": "~1.2.1", 205 | "safe-buffer": "5.1.2", 206 | "send": "0.17.1", 207 | "serve-static": "1.14.1", 208 | "setprototypeof": "1.1.1", 209 | "statuses": "~1.5.0", 210 | "type-is": "~1.6.18", 211 | "utils-merge": "1.0.1", 212 | "vary": "~1.1.2" 213 | } 214 | }, 215 | "finalhandler": { 216 | "version": "1.1.2", 217 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 218 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 219 | "requires": { 220 | "debug": "2.6.9", 221 | "encodeurl": "~1.0.2", 222 | "escape-html": "~1.0.3", 223 | "on-finished": "~2.3.0", 224 | "parseurl": "~1.3.3", 225 | "statuses": "~1.5.0", 226 | "unpipe": "~1.0.0" 227 | } 228 | }, 229 | "follow-redirects": { 230 | "version": "1.13.0", 231 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", 232 | "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" 233 | }, 234 | "forwarded": { 235 | "version": "0.1.2", 236 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 237 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 238 | }, 239 | "fresh": { 240 | "version": "0.5.2", 241 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 242 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 243 | }, 244 | "get-port": { 245 | "version": "5.1.1", 246 | "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", 247 | "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==" 248 | }, 249 | "http-errors": { 250 | "version": "1.7.2", 251 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 252 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 253 | "requires": { 254 | "depd": "~1.1.2", 255 | "inherits": "2.0.3", 256 | "setprototypeof": "1.1.1", 257 | "statuses": ">= 1.5.0 < 2", 258 | "toidentifier": "1.0.0" 259 | } 260 | }, 261 | "https": { 262 | "version": "1.0.0", 263 | "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", 264 | "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q=" 265 | }, 266 | "iconv-lite": { 267 | "version": "0.4.24", 268 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 269 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 270 | "requires": { 271 | "safer-buffer": ">= 2.1.2 < 3" 272 | } 273 | }, 274 | "inherits": { 275 | "version": "2.0.3", 276 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 277 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 278 | }, 279 | "ipaddr.js": { 280 | "version": "1.9.1", 281 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 282 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 283 | }, 284 | "is-buffer": { 285 | "version": "1.1.6", 286 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 287 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 288 | }, 289 | "isarray": { 290 | "version": "1.0.0", 291 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 292 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 293 | }, 294 | "jsonwebtoken": { 295 | "version": "8.5.1", 296 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", 297 | "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", 298 | "requires": { 299 | "jws": "^3.2.2", 300 | "lodash.includes": "^4.3.0", 301 | "lodash.isboolean": "^3.0.3", 302 | "lodash.isinteger": "^4.0.4", 303 | "lodash.isnumber": "^3.0.3", 304 | "lodash.isplainobject": "^4.0.6", 305 | "lodash.isstring": "^4.0.1", 306 | "lodash.once": "^4.0.0", 307 | "ms": "^2.1.1", 308 | "semver": "^5.6.0" 309 | }, 310 | "dependencies": { 311 | "ms": { 312 | "version": "2.1.2", 313 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 314 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 315 | } 316 | } 317 | }, 318 | "jwa": { 319 | "version": "1.4.1", 320 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", 321 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", 322 | "requires": { 323 | "buffer-equal-constant-time": "1.0.1", 324 | "ecdsa-sig-formatter": "1.0.11", 325 | "safe-buffer": "^5.0.1" 326 | } 327 | }, 328 | "jws": { 329 | "version": "3.2.2", 330 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", 331 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", 332 | "requires": { 333 | "jwa": "^1.4.1", 334 | "safe-buffer": "^5.0.1" 335 | } 336 | }, 337 | "kareem": { 338 | "version": "2.3.1", 339 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", 340 | "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" 341 | }, 342 | "lodash.includes": { 343 | "version": "4.3.0", 344 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 345 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 346 | }, 347 | "lodash.isboolean": { 348 | "version": "3.0.3", 349 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 350 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 351 | }, 352 | "lodash.isinteger": { 353 | "version": "4.0.4", 354 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 355 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 356 | }, 357 | "lodash.isnumber": { 358 | "version": "3.0.3", 359 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 360 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 361 | }, 362 | "lodash.isplainobject": { 363 | "version": "4.0.6", 364 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 365 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 366 | }, 367 | "lodash.isstring": { 368 | "version": "4.0.1", 369 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 370 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 371 | }, 372 | "lodash.once": { 373 | "version": "4.1.1", 374 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 375 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 376 | }, 377 | "md5": { 378 | "version": "2.3.0", 379 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", 380 | "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", 381 | "requires": { 382 | "charenc": "0.0.2", 383 | "crypt": "0.0.2", 384 | "is-buffer": "~1.1.6" 385 | } 386 | }, 387 | "media-typer": { 388 | "version": "0.3.0", 389 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 390 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 391 | }, 392 | "memory-pager": { 393 | "version": "1.5.0", 394 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 395 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 396 | "optional": true 397 | }, 398 | "merge-descriptors": { 399 | "version": "1.0.1", 400 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 401 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 402 | }, 403 | "methods": { 404 | "version": "1.1.2", 405 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 406 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 407 | }, 408 | "mime": { 409 | "version": "1.6.0", 410 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 411 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 412 | }, 413 | "mime-db": { 414 | "version": "1.44.0", 415 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 416 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 417 | }, 418 | "mime-types": { 419 | "version": "2.1.27", 420 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 421 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 422 | "requires": { 423 | "mime-db": "1.44.0" 424 | } 425 | }, 426 | "mongodb": { 427 | "version": "3.6.0", 428 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.0.tgz", 429 | "integrity": "sha512-/XWWub1mHZVoqEsUppE0GV7u9kanLvHxho6EvBxQbShXTKYF9trhZC2NzbulRGeG7xMJHD8IOWRcdKx5LPjAjQ==", 430 | "requires": { 431 | "bl": "^2.2.0", 432 | "bson": "^1.1.4", 433 | "denque": "^1.4.1", 434 | "require_optional": "^1.0.1", 435 | "safe-buffer": "^5.1.2", 436 | "saslprep": "^1.0.0" 437 | } 438 | }, 439 | "mongoose": { 440 | "version": "5.10.0", 441 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.0.tgz", 442 | "integrity": "sha512-5itAvBMVDG4+zTDtuLg/IyoTxEMgvpOSHnigQ9Cyh8LR4BEgMAChJj7JSaGkg+tr1AjCSY9DgSdU8bHqCOoxXg==", 443 | "requires": { 444 | "bson": "^1.1.4", 445 | "kareem": "2.3.1", 446 | "mongodb": "3.6.0", 447 | "mongoose-legacy-pluralize": "1.0.2", 448 | "mpath": "0.7.0", 449 | "mquery": "3.2.2", 450 | "ms": "2.1.2", 451 | "regexp-clone": "1.0.0", 452 | "safe-buffer": "5.2.1", 453 | "sift": "7.0.1", 454 | "sliced": "1.0.1" 455 | }, 456 | "dependencies": { 457 | "ms": { 458 | "version": "2.1.2", 459 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 460 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 461 | }, 462 | "safe-buffer": { 463 | "version": "5.2.1", 464 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 465 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 466 | } 467 | } 468 | }, 469 | "mongoose-legacy-pluralize": { 470 | "version": "1.0.2", 471 | "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", 472 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" 473 | }, 474 | "mpath": { 475 | "version": "0.7.0", 476 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", 477 | "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==" 478 | }, 479 | "mquery": { 480 | "version": "3.2.2", 481 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", 482 | "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", 483 | "requires": { 484 | "bluebird": "3.5.1", 485 | "debug": "3.1.0", 486 | "regexp-clone": "^1.0.0", 487 | "safe-buffer": "5.1.2", 488 | "sliced": "1.0.1" 489 | }, 490 | "dependencies": { 491 | "debug": { 492 | "version": "3.1.0", 493 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 494 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 495 | "requires": { 496 | "ms": "2.0.0" 497 | } 498 | } 499 | } 500 | }, 501 | "ms": { 502 | "version": "2.0.0", 503 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 504 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 505 | }, 506 | "negotiator": { 507 | "version": "0.6.2", 508 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 509 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 510 | }, 511 | "object-assign": { 512 | "version": "4.1.1", 513 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 514 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 515 | }, 516 | "on-finished": { 517 | "version": "2.3.0", 518 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 519 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 520 | "requires": { 521 | "ee-first": "1.1.1" 522 | } 523 | }, 524 | "parseurl": { 525 | "version": "1.3.3", 526 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 527 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 528 | }, 529 | "path-to-regexp": { 530 | "version": "0.1.7", 531 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 532 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 533 | }, 534 | "process-nextick-args": { 535 | "version": "2.0.1", 536 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 537 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 538 | }, 539 | "proxy-addr": { 540 | "version": "2.0.6", 541 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 542 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 543 | "requires": { 544 | "forwarded": "~0.1.2", 545 | "ipaddr.js": "1.9.1" 546 | } 547 | }, 548 | "qs": { 549 | "version": "6.7.0", 550 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 551 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 552 | }, 553 | "range-parser": { 554 | "version": "1.2.1", 555 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 556 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 557 | }, 558 | "raw-body": { 559 | "version": "2.4.0", 560 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 561 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 562 | "requires": { 563 | "bytes": "3.1.0", 564 | "http-errors": "1.7.2", 565 | "iconv-lite": "0.4.24", 566 | "unpipe": "1.0.0" 567 | } 568 | }, 569 | "readable-stream": { 570 | "version": "2.3.7", 571 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 572 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 573 | "requires": { 574 | "core-util-is": "~1.0.0", 575 | "inherits": "~2.0.3", 576 | "isarray": "~1.0.0", 577 | "process-nextick-args": "~2.0.0", 578 | "safe-buffer": "~5.1.1", 579 | "string_decoder": "~1.1.1", 580 | "util-deprecate": "~1.0.1" 581 | } 582 | }, 583 | "regexp-clone": { 584 | "version": "1.0.0", 585 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", 586 | "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" 587 | }, 588 | "require_optional": { 589 | "version": "1.0.1", 590 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 591 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 592 | "requires": { 593 | "resolve-from": "^2.0.0", 594 | "semver": "^5.1.0" 595 | } 596 | }, 597 | "resolve-from": { 598 | "version": "2.0.0", 599 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 600 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 601 | }, 602 | "safe-buffer": { 603 | "version": "5.1.2", 604 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 605 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 606 | }, 607 | "safer-buffer": { 608 | "version": "2.1.2", 609 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 610 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 611 | }, 612 | "saslprep": { 613 | "version": "1.0.3", 614 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 615 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 616 | "optional": true, 617 | "requires": { 618 | "sparse-bitfield": "^3.0.3" 619 | } 620 | }, 621 | "semver": { 622 | "version": "5.7.1", 623 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 624 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 625 | }, 626 | "send": { 627 | "version": "0.17.1", 628 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 629 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 630 | "requires": { 631 | "debug": "2.6.9", 632 | "depd": "~1.1.2", 633 | "destroy": "~1.0.4", 634 | "encodeurl": "~1.0.2", 635 | "escape-html": "~1.0.3", 636 | "etag": "~1.8.1", 637 | "fresh": "0.5.2", 638 | "http-errors": "~1.7.2", 639 | "mime": "1.6.0", 640 | "ms": "2.1.1", 641 | "on-finished": "~2.3.0", 642 | "range-parser": "~1.2.1", 643 | "statuses": "~1.5.0" 644 | }, 645 | "dependencies": { 646 | "ms": { 647 | "version": "2.1.1", 648 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 649 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 650 | } 651 | } 652 | }, 653 | "serve-static": { 654 | "version": "1.14.1", 655 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 656 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 657 | "requires": { 658 | "encodeurl": "~1.0.2", 659 | "escape-html": "~1.0.3", 660 | "parseurl": "~1.3.3", 661 | "send": "0.17.1" 662 | } 663 | }, 664 | "setprototypeof": { 665 | "version": "1.1.1", 666 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 667 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 668 | }, 669 | "sift": { 670 | "version": "7.0.1", 671 | "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", 672 | "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" 673 | }, 674 | "sliced": { 675 | "version": "1.0.1", 676 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 677 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 678 | }, 679 | "sparse-bitfield": { 680 | "version": "3.0.3", 681 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 682 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 683 | "optional": true, 684 | "requires": { 685 | "memory-pager": "^1.0.2" 686 | } 687 | }, 688 | "statuses": { 689 | "version": "1.5.0", 690 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 691 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 692 | }, 693 | "string_decoder": { 694 | "version": "1.1.1", 695 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 696 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 697 | "requires": { 698 | "safe-buffer": "~5.1.0" 699 | } 700 | }, 701 | "toidentifier": { 702 | "version": "1.0.0", 703 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 704 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 705 | }, 706 | "type-is": { 707 | "version": "1.6.18", 708 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 709 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 710 | "requires": { 711 | "media-typer": "0.3.0", 712 | "mime-types": "~2.1.24" 713 | } 714 | }, 715 | "unpipe": { 716 | "version": "1.0.0", 717 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 718 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 719 | }, 720 | "util-deprecate": { 721 | "version": "1.0.2", 722 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 723 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 724 | }, 725 | "utils-merge": { 726 | "version": "1.0.1", 727 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 728 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 729 | }, 730 | "vary": { 731 | "version": "1.1.2", 732 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 733 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 734 | } 735 | } 736 | } 737 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "singlelink", 3 | "version": "0.9.0", 4 | "description": "

\"SingleLink

\t\"Version\" \t\"License\"/ \t\"Build \t\"Users\"/

Welcome to the SingleLink Github repository. Here you can browse the source, view open issues and monitor development.

", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Neutron-Creative/SingleLink.git" 12 | }, 13 | "author": "Neutron Creative Inc.", 14 | "license": "GPL-V3", 15 | "bugs": { 16 | "url": "https://github.com/Neutron-Creative/SingleLink/issues" 17 | }, 18 | "homepage": "https://github.com/Neutron-Creative/SingleLink#readme", 19 | "dependencies": { 20 | "axios": "^0.20.0", 21 | "bcrypt-nodejs": "0.0.3", 22 | "body-parser": "^1.19.0", 23 | "cors": "^2.8.5", 24 | "express": "^4.17.1", 25 | "get-port": "^5.1.1", 26 | "https": "^1.0.0", 27 | "jsonwebtoken": "^8.5.1", 28 | "md5": "^2.3.0", 29 | "mongoose": "^5.10.0" 30 | } 31 | } 32 | --------------------------------------------------------------------------------