├── .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 |

2 |
3 |
4 |
5 |
6 |
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 |
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 |
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": "
\t
\t
\t
\t
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 |
--------------------------------------------------------------------------------