diff options
author | Max Wu | 2017-05-14 17:50:25 +0800 |
---|---|---|
committer | GitHub | 2017-05-14 17:50:25 +0800 |
commit | ea9b1557f9834b2b140bd9b07488b5fca3953791 (patch) | |
tree | 8e96eb37c1715f860a698d5e7eeb2381b00af9d8 | |
parent | f85d1d880114b6787fdb2bac488341776deaad3d (diff) | |
parent | c37b6669158750ab2222e81a5a8fc3a5c8c353f5 (diff) |
Merge pull request #421 from hackmdio/BackendRefactor
Backend code refactoring - part 1
Diffstat (limited to '')
45 files changed, 1590 insertions, 1556 deletions
diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 121531af..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -*.min.js diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index bd14731c..00000000 --- a/.eslintrc +++ /dev/null @@ -1,268 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "node": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "accessor-pairs": "error", - "array-bracket-spacing": [ - "error", - "never" - ], - "array-callback-return": "error", - "arrow-body-style": "error", - "arrow-spacing": "error", - "block-scoped-var": "off", - "block-spacing": "error", - "brace-style": [ - "error", - "1tbs" - ], - "callback-return": "off", - "camelcase": "error", - "class-methods-use-this": "error", - "comma-dangle": "error", - "comma-spacing": [ - "error", { - "after": true, - "before": false - } - ], - "comma-style": [ - "error", - "last" - ], - "complexity": "error", - "computed-property-spacing": [ - "error", - "never" - ], - "consistent-return": "off", - "consistent-this": "error", - "curly": "off", - "default-case": "error", - "dot-location": [ - "error", - "property" - ], - "dot-notation": [ - "error", { - "allowKeywords": true - } - ], - "eol-last": "error", - "eqeqeq": "off", - "func-call-spacing": "error", - "func-names": [ - "error", - "never" - ], - "func-style": [ - "error", - "declaration" - ], - "generator-star-spacing": "error", - "global-require": "off", - "guard-for-in": "error", - "handle-callback-err": "error", - "id-blacklist": "error", - "id-length": "off", - "id-match": "error", - "indent": 2, - "init-declarations": "off", - "jsx-quotes": "error", - "key-spacing": "error", - "keyword-spacing": [ - "error", { - "after": true, - "before": true - } - ], - "line-comment-position": "off", - "linebreak-style": [ - "error", - "unix" - ], - "lines-around-comment": "error", - "lines-around-directive": "error", - "max-depth": "error", - "max-len": "off", - "max-lines": "off", - "max-nested-callbacks": "error", - "max-params": "error", - "max-statements": "error", - "max-statements-per-line": "error", - "multiline-ternary": [ - "error", - "never" - ], - "new-cap": "error", - "new-parens": "error", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "off", - "no-alert": "error", - "no-array-constructor": "error", - "no-bitwise": "error", - "no-caller": "error", - "no-catch-shadow": "off", - "no-confusing-arrow": "error", - "no-continue": "error", - "no-div-regex": "error", - "no-duplicate-imports": "error", - "no-else-return": "error", - "no-empty-function": "error", - "no-eq-null": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-extra-parens": "warn", - "no-floating-decimal": "error", - "no-global-assign": "error", - "no-implicit-coercion": "error", - "no-implicit-globals": "error", - "no-implied-eval": "error", - "no-inline-comments": "off", - "no-inner-declarations": [ - "error", - "functions" - ], - "no-invalid-this": "error", - "no-iterator": "error", - "no-label-var": "error", - "no-labels": "error", - "no-lone-blocks": "error", - "no-lonely-if": "error", - "no-loop-func": "error", - "no-magic-numbers": "off", - "no-mixed-operators": "error", - "no-mixed-requires": "error", - "no-multi-spaces": "error", - "no-multi-str": "error", - "no-multiple-empty-lines": "error", - "no-negated-condition": "off", - "no-nested-ternary": "error", - "no-new": "error", - "no-new-func": "error", - "no-new-object": "error", - "no-new-require": "error", - "no-new-wrappers": "error", - "no-octal-escape": "error", - "no-param-reassign": [ - "error", { - "props": false - } - ], - "no-path-concat": "off", - "no-plusplus": [ - "error", { - "allowForLoopAfterthoughts": true - } - ], - "no-process-env": "error", - "no-process-exit": "off", - "no-proto": "error", - "no-prototype-builtins": "error", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-modules": "error", - "no-restricted-properties": "error", - "no-restricted-syntax": "error", - "no-return-assign": "error", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "error", - "no-shadow": "off", - "no-shadow-restricted-names": "error", - "no-spaced-func": "error", - "no-sync": "off", - "no-tabs": "error", - "no-template-curly-in-string": "error", - "no-ternary": "off", - "no-throw-literal": "error", - "no-trailing-spaces": "error", - "no-undef-init": "error", - "no-undefined": "error", - "no-underscore-dangle": "error", - "no-unmodified-loop-condition": "error", - "no-unneeded-ternary": "error", - "no-unsafe-negation": "error", - "no-unused-expressions": "error", - "no-use-before-define": "warn", - "no-useless-call": "error", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-constructor": "error", - "no-useless-escape": "error", - "no-useless-rename": "error", - "no-var": "off", - "no-void": "error", - "no-warning-comments": "error", - "no-whitespace-before-property": "error", - "no-with": "error", - "object-curly-newline": "off", - "object-curly-spacing": [ - "error", - "always" - ], - "object-property-newline": "error", - "object-shorthand": "off", - "one-var": "off", - "one-var-declaration-per-line": [ - "error", - "initializations" - ], - "operator-assignment": "error", - "operator-linebreak": "error", - "padded-blocks": "off", - "prefer-arrow-callback": "off", - "prefer-const": "error", - "prefer-numeric-literals": "error", - "prefer-reflect": "error", - "prefer-rest-params": "error", - "prefer-spread": "error", - "prefer-template": "off", - "quote-props": "off", - "quotes": "off", - "radix": "error", - "require-jsdoc": "off", - "rest-spread-spacing": "error", - "semi": "off", - "semi-spacing": [ - "error", { - "after": true, - "before": false - } - ], - "sort-imports": "error", - "sort-keys": "off", - "sort-vars": "error", - "space-before-blocks": "error", - "space-before-function-paren": "off", - "space-in-parens": [ - "error", - "never" - ], - "space-infix-ops": "error", - "space-unary-ops": "error", - "spaced-comment": "off", - "strict": "error", - "symbol-description": "error", - "template-curly-spacing": "error", - "unicode-bom": [ - "error", - "never" - ], - "valid-jsdoc": "error", - "vars-on-top": "off", - "wrap-regex": "error", - "yield-star-spacing": "error", - "yoda": "off" - } -} @@ -1,52 +1,42 @@ +'use strict' // app // external modules var express = require('express') -var toobusy = require('toobusy-js') + var ejs = require('ejs') var passport = require('passport') var methodOverride = require('method-override') var cookieParser = require('cookie-parser') -var bodyParser = require('body-parser') var compression = require('compression') var session = require('express-session') var SequelizeStore = require('connect-session-sequelize')(session.Store) var fs = require('fs') -var url = require('url') var path = require('path') -var imgur = require('imgur') -var formidable = require('formidable') + var morgan = require('morgan') var passportSocketIo = require('passport.socketio') var helmet = require('helmet') var i18n = require('i18n') var flash = require('connect-flash') -var validator = require('validator') - -// utils -var getImageMimeType = require('./lib/utils.js').getImageMimeType // core -var config = require('./lib/config.js') -var logger = require('./lib/logger.js') -var auth = require('./lib/auth.js') -var response = require('./lib/response.js') +var config = require('./lib/config') +var logger = require('./lib/logger') +var response = require('./lib/response') var models = require('./lib/models') // generate front-end constants by template -var configJson = config.raw var constpath = path.join(__dirname, './public/js/lib/common/constant.ejs') -var googleApiKey = (fs.existsSync('/run/secrets/google_apiKey') && config.handleDockerSecret('google_apiKey')) || process.env.HMD_GOOGLE_API_KEY || (configJson.google && configJson.google.apiKey) || '' -var googleClientID = (fs.existsSync('/run/secrets/google_clientID') && config.handleDockerSecret('google_clientID')) || process.env.HMD_GOOGLE_CLIENT_ID || (configJson.google && configJson.google.clientID) || '' -var dropboxAppKey = (fs.existsSync('/run/secrets/dropbox_appKey') && config.handleDockerSecret('dropbox_appKey')) || process.env.HMD_DROPBOX_APP_KEY || (configJson.dropbox && configJson.dropbox.appKey) || '' var data = { domain: config.domain, urlpath: config.urlpath, debug: config.debug, version: config.version, - GOOGLE_API_KEY: googleApiKey, - GOOGLE_CLIENT_ID: googleClientID, - DROPBOX_APP_KEY: dropboxAppKey + GOOGLE_API_KEY: config.google.clientSecret, + GOOGLE_CLIENT_ID: config.google.clientID, + DROPBOX_APP_KEY: config.dropbox.clientSecret } + ejs.renderFile(constpath, data, {}, function (err, str) { if (err) throw new Error(err) fs.writeFileSync(path.join(__dirname, './public/build/constant.js'), str) @@ -79,7 +69,7 @@ if (config.usessl) { // logger app.use(morgan('combined', { - 'stream': logger.stream + 'stream': logger })) // socket io @@ -98,12 +88,6 @@ realtime.io = io // methodOverride app.use(methodOverride('_method')) -// create application/x-www-form-urlencoded parser -var urlencodedParser = bodyParser.urlencoded({ - extended: false, - limit: 1024 * 1024 * 10 // 10 mb -}) - // session store var sessionStore = new SequelizeStore({ db: models.sequelize @@ -157,63 +141,18 @@ server.on('resumeSession', function (id, cb) { }) // middleware which blocks requests when we're too busy -app.use(function (req, res, next) { - if (toobusy()) { - response.errorServiceUnavailable(res) - } else { - next() - } -}) +app.use(require('./lib/web/middleware/tooBusy')) app.use(flash()) // passport app.use(passport.initialize()) app.use(passport.session()) -auth.registerAuthMethod() - -// serialize and deserialize -passport.serializeUser(function (user, done) { - logger.info('serializeUser: ' + user.id) - return done(null, user.id) -}) -passport.deserializeUser(function (id, done) { - models.User.findOne({ - where: { - id: id - } - }).then(function (user) { - logger.info('deserializeUser: ' + user.id) - return done(null, user) - }).catch(function (err) { - logger.error(err) - return done(err, null) - }) -}) // check uri is valid before going further -app.use(function (req, res, next) { - try { - decodeURIComponent(req.path) - } catch (err) { - logger.error(err) - return response.errorBadRequest(res) - } - next() -}) - +app.use(require('./lib/web/middleware/checkURIValid')) // redirect url without trailing slashes -app.use(function (req, res, next) { - if (req.method === 'GET' && req.path.substr(-1) === '/' && req.path.length > 1) { - var query = req.url.slice(req.path.length) - var urlpath = req.path.slice(0, -1) - var serverurl = config.serverurl - if (config.urlpath) serverurl = serverurl.slice(0, -(config.urlpath.length + 1)) - res.redirect(301, serverurl + urlpath + query) - } else { - next() - } -}) +app.use(require('./lib/web/middleware/redirectWithoutTrailingSlashes')) // routes need sessions // template files @@ -222,390 +161,16 @@ app.set('views', path.join(__dirname, '/public/views')) app.engine('ejs', ejs.renderFile) // set view engine app.set('view engine', 'ejs') -// get index -app.get('/', response.showIndex) -// get 403 forbidden -app.get('/403', function (req, res) { - response.errorForbidden(res) -}) -// get 404 not found -app.get('/404', function (req, res) { - response.errorNotFound(res) -}) -// get 500 internal error -app.get('/500', function (req, res) { - response.errorInternalError(res) -}) -// get status -app.get('/status', function (req, res, next) { - realtime.getStatus(function (data) { - res.set({ - 'Cache-Control': 'private', // only cache by client - 'X-Robots-Tag': 'noindex, nofollow', // prevent crawling - 'HackMD-Version': config.version - }) - res.send(data) - }) -}) -// get status -app.get('/temp', function (req, res) { - var host = req.get('host') - if (config.alloworigin.indexOf(host) === -1) { - response.errorForbidden(res) - } else { - var tempid = req.query.tempid - if (!tempid) { - response.errorForbidden(res) - } else { - models.Temp.findOne({ - where: { - id: tempid - } - }).then(function (temp) { - if (!temp) { - response.errorNotFound(res) - } else { - res.header('Access-Control-Allow-Origin', '*') - res.send({ - temp: temp.data - }) - temp.destroy().catch(function (err) { - if (err) { - logger.error('remove temp failed: ' + err) - } - }) - } - }).catch(function (err) { - logger.error(err) - return response.errorInternalError(res) - }) - } - } -}) -// post status -app.post('/temp', urlencodedParser, function (req, res) { - var host = req.get('host') - if (config.alloworigin.indexOf(host) === -1) { - response.errorForbidden(res) - } else { - var data = req.body.data - if (!data) { - response.errorForbidden(res) - } else { - if (config.debug) { - logger.info('SERVER received temp from [' + host + ']: ' + req.body.data) - } - models.Temp.create({ - data: data - }).then(function (temp) { - if (temp) { - res.header('Access-Control-Allow-Origin', '*') - res.send({ - status: 'ok', - id: temp.id - }) - } else { - response.errorInternalError(res) - } - }).catch(function (err) { - logger.error(err) - return response.errorInternalError(res) - }) - } - } -}) -function setReturnToFromReferer (req) { - var referer = req.get('referer') - if (!req.session) req.session = {} - req.session.returnTo = referer -} - -// facebook auth -if (config.facebook) { - app.get('/auth/facebook', function (req, res, next) { - setReturnToFromReferer(req) - passport.authenticate('facebook')(req, res, next) - }) - // facebook auth callback - app.get('/auth/facebook/callback', - passport.authenticate('facebook', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/' - })) -} -// twitter auth -if (config.twitter) { - app.get('/auth/twitter', function (req, res, next) { - setReturnToFromReferer(req) - passport.authenticate('twitter')(req, res, next) - }) - // twitter auth callback - app.get('/auth/twitter/callback', - passport.authenticate('twitter', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/' - })) -} -// github auth -if (config.github) { - app.get('/auth/github', function (req, res, next) { - setReturnToFromReferer(req) - passport.authenticate('github')(req, res, next) - }) - // github auth callback - app.get('/auth/github/callback', - passport.authenticate('github', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/' - })) - if (!config.gitlab.scope || config.gitlab.scope === 'api') { - // gitlab callback actions - app.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions) - } -} -// gitlab auth -if (config.gitlab) { - app.get('/auth/gitlab', function (req, res, next) { - setReturnToFromReferer(req) - passport.authenticate('gitlab')(req, res, next) - }) - // gitlab auth callback - app.get('/auth/gitlab/callback', - passport.authenticate('gitlab', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/' - })) - // gitlab callback actions - app.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions) -} -// dropbox auth -if (config.dropbox) { - app.get('/auth/dropbox', function (req, res, next) { - setReturnToFromReferer(req) - passport.authenticate('dropbox-oauth2')(req, res, next) - }) - // dropbox auth callback - app.get('/auth/dropbox/callback', - passport.authenticate('dropbox-oauth2', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/' - })) -} -// google auth -if (config.google) { - app.get('/auth/google', function (req, res, next) { - setReturnToFromReferer(req) - passport.authenticate('google', { scope: ['profile'] })(req, res, next) - }) - // google auth callback - app.get('/auth/google/callback', - passport.authenticate('google', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/' - })) -} -// ldap auth -if (config.ldap) { - app.post('/auth/ldap', urlencodedParser, function (req, res, next) { - if (!req.body.username || !req.body.password) return response.errorBadRequest(res) - setReturnToFromReferer(req) - passport.authenticate('ldapauth', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/', - failureFlash: true - })(req, res, next) - }) -} -// email auth -if (config.email) { - if (config.allowemailregister) { - app.post('/register', urlencodedParser, function (req, res, next) { - if (!req.body.email || !req.body.password) return response.errorBadRequest(res) - if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res) - models.User.findOrCreate({ - where: { - email: req.body.email - }, - defaults: { - password: req.body.password - } - }).spread(function (user, created) { - if (user) { - if (created) { - if (config.debug) { - logger.info('user registered: ' + user.id) - } - req.flash('info', "You've successfully registered, please signin.") - } else { - if (config.debug) { - logger.info('user found: ' + user.id) - } - req.flash('error', 'This email has been used, please try another one.') - } - return res.redirect(config.serverurl + '/') - } - req.flash('error', 'Failed to register your account, please try again.') - return res.redirect(config.serverurl + '/') - }).catch(function (err) { - logger.error('auth callback failed: ' + err) - return response.errorInternalError(res) - }) - }) - } - - app.post('/login', urlencodedParser, function (req, res, next) { - if (!req.body.email || !req.body.password) return response.errorBadRequest(res) - if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res) - setReturnToFromReferer(req) - passport.authenticate('local', { - successReturnToOrRedirect: config.serverurl + '/', - failureRedirect: config.serverurl + '/', - failureFlash: 'Invalid email or password.' - })(req, res, next) - }) -} -// logout -app.get('/logout', function (req, res) { - if (config.debug && req.isAuthenticated()) { logger.info('user logout: ' + req.user.id) } - req.logout() - res.redirect(config.serverurl + '/') -}) -var history = require('./lib/history.js') -// get history -app.get('/history', history.historyGet) -// post history -app.post('/history', urlencodedParser, history.historyPost) -// post history by note id -app.post('/history/:noteId', urlencodedParser, history.historyPost) -// delete history -app.delete('/history', history.historyDelete) -// delete history by note id -app.delete('/history/:noteId', history.historyDelete) -// get me info -app.get('/me', function (req, res) { - if (req.isAuthenticated()) { - models.User.findOne({ - where: { - id: req.user.id - } - }).then(function (user) { - if (!user) { return response.errorNotFound(res) } - var profile = models.User.getProfile(user) - res.send({ - status: 'ok', - id: req.user.id, - name: profile.name, - photo: profile.photo - }) - }).catch(function (err) { - logger.error('read me failed: ' + err) - return response.errorInternalError(res) - }) - } else { - res.send({ - status: 'forbidden' - }) - } -}) - -// upload image -app.post('/uploadimage', function (req, res) { - var form = new formidable.IncomingForm() - - form.keepExtensions = true - - if (config.imageUploadType === 'filesystem') { - form.uploadDir = 'public/uploads' - } +app.use(require('./lib/web/baseRouter')) +app.use(require('./lib/web/statusRouter')) +app.use(require('./lib/web/auth')) +app.use(require('./lib/web/historyRouter')) +app.use(require('./lib/web/userRouter')) +app.use(require('./lib/web/imageRouter')) +app.use(require('./lib/web/noteRouter')) - form.parse(req, function (err, fields, files) { - if (err || !files.image || !files.image.path) { - response.errorForbidden(res) - } else { - if (config.debug) { logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)) } - - try { - switch (config.imageUploadType) { - case 'filesystem': - res.send({ - link: url.resolve(config.serverurl + '/', files.image.path.match(/^public\/(.+$)/)[1]) - }) - - break - - case 's3': - var AWS = require('aws-sdk') - var awsConfig = new AWS.Config(config.s3) - var s3 = new AWS.S3(awsConfig) - - fs.readFile(files.image.path, function (err, buffer) { - if (err) { - logger.error(err) - res.status(500).end('upload image error') - return - } - var params = { - Bucket: config.s3bucket, - Key: path.join('uploads', path.basename(files.image.path)), - Body: buffer - } - - var mimeType = getImageMimeType(files.image.path) - if (mimeType) { params.ContentType = mimeType } - - s3.putObject(params, function (err, data) { - if (err) { - logger.error(err) - res.status(500).end('upload image error') - return - } - res.send({ - link: `https://s3-${config.s3.region}.amazonaws.com/${config.s3bucket}/${params.Key}` - }) - }) - }) - break - case 'imgur': - default: - imgur.setClientId(config.imgur.clientID) - imgur.uploadFile(files.image.path) - .then(function (json) { - if (config.debug) { logger.info('SERVER uploadimage success: ' + JSON.stringify(json)) } - res.send({ - link: json.data.link.replace(/^http:\/\//i, 'https://') - }) - }) - .catch(function (err) { - logger.error(err) - return res.status(500).end('upload image error') - }) - break - } - } catch (err) { - logger.error(err) - return res.status(500).end('upload image error') - } - } - }) -}) -// get new note -app.get('/new', response.newNote) -// get publish note -app.get('/s/:shortid', response.showPublishNote) -// publish note actions -app.get('/s/:shortid/:action', response.publishNoteActions) -// get publish slide -app.get('/p/:shortid', response.showPublishSlide) -// publish slide actions -app.get('/p/:shortid/:action', response.publishSlideActions) -// get note by id -app.get('/:noteId', response.showNote) -// note actions -app.get('/:noteId/:action', response.noteActions) -// note actions with action id -app.get('/:noteId/:action/:actionId', response.noteActions) -// response not found if no any route matches +// response not found if no any route matxches app.get('*', function (req, res) { response.errorNotFound(res) }) @@ -632,7 +197,7 @@ function startListen () { server.listen(config.port, function () { var schema = config.usessl ? 'HTTPS' : 'HTTP' logger.info('%s Server listening at port %d', schema, config.port) - config.maintenance = false + realtime.maintenance = false }) } @@ -660,7 +225,7 @@ process.on('uncaughtException', function (err) { // install exit handler function handleTermSignals () { logger.info('hackmd has been killed by signal, try to exit gracefully...') - config.maintenance = true + realtime.maintenance = true // disconnect all socket.io clients Object.keys(io.sockets.sockets).forEach(function (key) { var socket = io.sockets.sockets[key] diff --git a/lib/auth.js b/lib/auth.js deleted file mode 100644 index 3e129b95..00000000 --- a/lib/auth.js +++ /dev/null @@ -1,194 +0,0 @@ -'use strict' -// auth -// external modules -var passport = require('passport') -var FacebookStrategy = require('passport-facebook').Strategy -var TwitterStrategy = require('passport-twitter').Strategy -var GithubStrategy = require('passport-github').Strategy -var GitlabStrategy = require('passport-gitlab2').Strategy -var DropboxStrategy = require('passport-dropbox-oauth2').Strategy -var GoogleStrategy = require('passport-google-oauth20').Strategy -var LdapStrategy = require('passport-ldapauth') -var LocalStrategy = require('passport-local').Strategy -var validator = require('validator') - -// core -var config = require('./config.js') -var logger = require('./logger.js') -var models = require('./models') - -function callback (accessToken, refreshToken, profile, done) { - // logger.info(profile.displayName || profile.username); - var stringifiedProfile = JSON.stringify(profile) - models.User.findOrCreate({ - where: { - profileid: profile.id.toString() - }, - defaults: { - profile: stringifiedProfile, - accessToken: accessToken, - refreshToken: refreshToken - } - }).spread(function (user, created) { - if (user) { - var needSave = false - if (user.profile !== stringifiedProfile) { - user.profile = stringifiedProfile - needSave = true - } - if (user.accessToken !== accessToken) { - user.accessToken = accessToken - needSave = true - } - if (user.refreshToken !== refreshToken) { - user.refreshToken = refreshToken - needSave = true - } - if (needSave) { - user.save().then(function () { - if (config.debug) { logger.info('user login: ' + user.id) } - return done(null, user) - }) - } else { - if (config.debug) { logger.info('user login: ' + user.id) } - return done(null, user) - } - } - }).catch(function (err) { - logger.error('auth callback failed: ' + err) - return done(err, null) - }) -} - -function registerAuthMethod () { -// facebook - if (config.facebook) { - passport.use(new FacebookStrategy({ - clientID: config.facebook.clientID, - clientSecret: config.facebook.clientSecret, - callbackURL: config.serverurl + '/auth/facebook/callback' - }, callback)) - } -// twitter - if (config.twitter) { - passport.use(new TwitterStrategy({ - consumerKey: config.twitter.consumerKey, - consumerSecret: config.twitter.consumerSecret, - callbackURL: config.serverurl + '/auth/twitter/callback' - }, callback)) - } -// github - if (config.github) { - passport.use(new GithubStrategy({ - clientID: config.github.clientID, - clientSecret: config.github.clientSecret, - callbackURL: config.serverurl + '/auth/github/callback' - }, callback)) - } -// gitlab - if (config.gitlab) { - passport.use(new GitlabStrategy({ - baseURL: config.gitlab.baseURL, - clientID: config.gitlab.clientID, - clientSecret: config.gitlab.clientSecret, - scope: config.gitlab.scope, - callbackURL: config.serverurl + '/auth/gitlab/callback' - }, callback)) - } -// dropbox - if (config.dropbox) { - passport.use(new DropboxStrategy({ - apiVersion: '2', - clientID: config.dropbox.clientID, - clientSecret: config.dropbox.clientSecret, - callbackURL: config.serverurl + '/auth/dropbox/callback' - }, callback)) - } -// google - if (config.google) { - passport.use(new GoogleStrategy({ - clientID: config.google.clientID, - clientSecret: config.google.clientSecret, - callbackURL: config.serverurl + '/auth/google/callback' - }, callback)) - } -// ldap - if (config.ldap) { - passport.use(new LdapStrategy({ - server: { - url: config.ldap.url || null, - bindDn: config.ldap.bindDn || null, - bindCredentials: config.ldap.bindCredentials || null, - searchBase: config.ldap.searchBase || null, - searchFilter: config.ldap.searchFilter || null, - searchAttributes: config.ldap.searchAttributes || null, - tlsOptions: config.ldap.tlsOptions || null - } - }, - function (user, done) { - var profile = { - id: 'LDAP-' + user.uidNumber, - username: user.uid, - displayName: user.displayName, - emails: user.mail ? [user.mail] : [], - avatarUrl: null, - profileUrl: null, - provider: 'ldap' - } - var stringifiedProfile = JSON.stringify(profile) - models.User.findOrCreate({ - where: { - profileid: profile.id.toString() - }, - defaults: { - profile: stringifiedProfile - } - }).spread(function (user, created) { - if (user) { - var needSave = false - if (user.profile !== stringifiedProfile) { - user.profile = stringifiedProfile - needSave = true - } - if (needSave) { - user.save().then(function () { - if (config.debug) { logger.info('user login: ' + user.id) } - return done(null, user) - }) - } else { - if (config.debug) { logger.info('user login: ' + user.id) } - return done(null, user) - } - } - }).catch(function (err) { - logger.error('ldap auth failed: ' + err) - return done(err, null) - }) - })) - } -// email - if (config.email) { - passport.use(new LocalStrategy({ - usernameField: 'email' - }, - function (email, password, done) { - if (!validator.isEmail(email)) return done(null, false) - models.User.findOne({ - where: { - email: email - } - }).then(function (user) { - if (!user) return done(null, false) - if (!user.verifyPassword(password)) return done(null, false) - return done(null, user) - }).catch(function (err) { - logger.error(err) - return done(err) - }) - })) - } -} - -module.exports = { - registerAuthMethod: registerAuthMethod -} diff --git a/lib/config.js b/lib/config.js deleted file mode 100644 index 31999b07..00000000 --- a/lib/config.js +++ /dev/null @@ -1,223 +0,0 @@ -'use strict' -// external modules -var fs = require('fs') -var path = require('path') - -// configs -var env = process.env.NODE_ENV || 'development' -var config = require(path.join(__dirname, '..', 'config.json'))[env] -var debug = process.env.DEBUG ? (process.env.DEBUG === 'true') : ((typeof config.debug === 'boolean') ? config.debug : (env === 'development')) - -// Create function that reads docker secrets but fails fast in case of a non docker environment -var handleDockerSecret = fs.existsSync('/run/secrets/') ? function (secret) { - return fs.existsSync('/run/secrets/' + secret) ? fs.readFileSync('/run/secrets/' + secret) : null -} : function () { - return null -} - -// url -var domain = process.env.DOMAIN || process.env.HMD_DOMAIN || config.domain || '' -var urlpath = process.env.URL_PATH || process.env.HMD_URL_PATH || config.urlpath || '' -var port = process.env.PORT || process.env.HMD_PORT || config.port || 3000 -var alloworigin = process.env.HMD_ALLOW_ORIGIN ? process.env.HMD_ALLOW_ORIGIN.split(',') : (config.alloworigin || ['localhost']) - -var usessl = !!config.usessl -var protocolusessl = (usessl === true && typeof process.env.HMD_PROTOCOL_USESSL === 'undefined' && typeof config.protocolusessl === 'undefined') - ? true : (process.env.HMD_PROTOCOL_USESSL ? (process.env.HMD_PROTOCOL_USESSL === 'true') : !!config.protocolusessl) -var urladdport = process.env.HMD_URL_ADDPORT ? (process.env.HMD_URL_ADDPORT === 'true') : !!config.urladdport - -var usecdn = process.env.HMD_USECDN ? (process.env.HMD_USECDN === 'true') : ((typeof config.usecdn === 'boolean') ? config.usecdn : true) - -var allowanonymous = process.env.HMD_ALLOW_ANONYMOUS ? (process.env.HMD_ALLOW_ANONYMOUS === 'true') : ((typeof config.allowanonymous === 'boolean') ? config.allowanonymous : true) - -var allowfreeurl = process.env.HMD_ALLOW_FREEURL ? (process.env.HMD_ALLOW_FREEURL === 'true') : !!config.allowfreeurl - -var permissions = ['editable', 'limited', 'locked', 'protected', 'private'] -if (allowanonymous) { - permissions.unshift('freely') -} - -var defaultpermission = process.env.HMD_DEFAULT_PERMISSION || config.defaultpermission -defaultpermission = permissions.indexOf(defaultpermission) !== -1 ? defaultpermission : 'editable' - -// db -var dburl = process.env.HMD_DB_URL || process.env.DATABASE_URL || config.dburl -var db = config.db || {} - -// ssl path -var sslkeypath = (fs.existsSync('/run/secrets/key.pem') ? '/run/secrets/key.pem' : null) || config.sslkeypath || '' -var sslcertpath = (fs.existsSync('/run/secrets/cert.pem') ? '/run/secrets/cert.pem' : null) || config.sslcertpath || '' -var sslcapath = (fs.existsSync('/run/secrets/ca.pem') ? '/run/secrets/ca.pem' : null) || config.sslcapath || '' -var dhparampath = (fs.existsSync('/run/secrets/dhparam.pem') ? '/run/secrets/dhparam.pem' : null) || config.dhparampath || '' - -// other path -var tmppath = config.tmppath || './tmp' -var defaultnotepath = config.defaultnotepath || './public/default.md' -var docspath = config.docspath || './public/docs' -var indexpath = config.indexpath || './public/views/index.ejs' -var hackmdpath = config.hackmdpath || './public/views/hackmd.ejs' -var errorpath = config.errorpath || './public/views/error.ejs' -var prettypath = config.prettypath || './public/views/pretty.ejs' -var slidepath = config.slidepath || './public/views/slide.ejs' - -// session -var sessionname = config.sessionname || 'connect.sid' -var sessionsecret = handleDockerSecret('sessionsecret') || config.sessionsecret || 'secret' -var sessionlife = config.sessionlife || 14 * 24 * 60 * 60 * 1000 // 14 days - -// static files -var staticcachetime = config.staticcachetime || 1 * 24 * 60 * 60 * 1000 // 1 day - -// socket.io -var heartbeatinterval = config.heartbeatinterval || 5000 -var heartbeattimeout = config.heartbeattimeout || 10000 - -// document -var documentmaxlength = config.documentmaxlength || 100000 - -// image upload setting, available options are imgur/s3/filesystem -var imageUploadType = process.env.HMD_IMAGE_UPLOAD_TYPE || config.imageUploadType || 'imgur' - -config.s3 = config.s3 || {} -var s3 = { - accessKeyId: handleDockerSecret('s3_acccessKeyId') || process.env.HMD_S3_ACCESS_KEY_ID || config.s3.accessKeyId, - secretAccessKey: handleDockerSecret('s3_secretAccessKey') || process.env.HMD_S3_SECRET_ACCESS_KEY || config.s3.secretAccessKey, - region: process.env.HMD_S3_REGION || config.s3.region -} -var s3bucket = process.env.HMD_S3_BUCKET || config.s3.bucket - -// auth -var facebook = ((process.env.HMD_FACEBOOK_CLIENTID && process.env.HMD_FACEBOOK_CLIENTSECRET) || (fs.existsSync('/run/secrets/facebook_clientID') && fs.existsSync('/run/secrets/facebook_clientSecret'))) ? { - clientID: handleDockerSecret('facebook_clientID') || process.env.HMD_FACEBOOK_CLIENTID, - clientSecret: handleDockerSecret('facebook_clientSecret') || process.env.HMD_FACEBOOK_CLIENTSECRET -} : config.facebook || false -var twitter = ((process.env.HMD_TWITTER_CONSUMERKEY && process.env.HMD_TWITTER_CONSUMERSECRET) || (fs.existsSync('/run/secrets/twitter_consumerKey') && fs.existsSync('/run/secrets/twitter_consumerSecret'))) ? { - consumerKey: handleDockerSecret('twitter_consumerKey') || process.env.HMD_TWITTER_CONSUMERKEY, - consumerSecret: handleDockerSecret('twitter_consumerSecret') || process.env.HMD_TWITTER_CONSUMERSECRET -} : config.twitter || false -var github = ((process.env.HMD_GITHUB_CLIENTID && process.env.HMD_GITHUB_CLIENTSECRET) || (fs.existsSync('/run/secrets/github_clientID') && fs.existsSync('/run/secrets/github_clientSecret'))) ? { - clientID: handleDockerSecret('github_clientID') || process.env.HMD_GITHUB_CLIENTID, - clientSecret: handleDockerSecret('github_clientSecret') || process.env.HMD_GITHUB_CLIENTSECRET -} : config.github || false -var gitlab = ((process.env.HMD_GITLAB_CLIENTID && process.env.HMD_GITLAB_CLIENTSECRET) || (fs.existsSync('/run/secrets/gitlab_clientID') && fs.existsSync('/run/secrets/gitlab_clientSecret'))) ? { - baseURL: process.env.HMD_GITLAB_BASEURL, - clientID: handleDockerSecret('gitlab_clientID') || process.env.HMD_GITLAB_CLIENTID, - clientSecret: handleDockerSecret('gitlab_clientSecret') || process.env.HMD_GITLAB_CLIENTSECRET, - scope: process.env.HMD_GITLAB_SCOPE -} : (config.gitlab && config.gitlab.clientID && config.gitlab.clientSecret && config.gitlab) || false -var dropbox = ((process.env.HMD_DROPBOX_CLIENTID && process.env.HMD_DROPBOX_CLIENTSECRET) || (fs.existsSync('/run/secrets/dropbox_clientID') && fs.existsSync('/run/secrets/dropbox_clientSecret'))) ? { - clientID: handleDockerSecret('dropbox_clientID') || process.env.HMD_DROPBOX_CLIENTID, - clientSecret: handleDockerSecret('dropbox_clientSecret') || process.env.HMD_DROPBOX_CLIENTSECRET -} : (config.dropbox && config.dropbox.clientID && config.dropbox.clientSecret && config.dropbox) || false -var google = ((process.env.HMD_GOOGLE_CLIENTID && process.env.HMD_GOOGLE_CLIENTSECRET) || - (fs.existsSync('/run/secrets/google_clientID') && fs.existsSync('/run/secrets/google_clientSecret'))) ? { - clientID: handleDockerSecret('google_clientID') || process.env.HMD_GOOGLE_CLIENTID, - clientSecret: handleDockerSecret('google_clientSecret') || process.env.HMD_GOOGLE_CLIENTSECRET - } : (config.google && config.google.clientID && config.google.clientSecret && config.google) || false -var ldap = config.ldap || (( - process.env.HMD_LDAP_URL || - process.env.HMD_LDAP_BINDDN || - process.env.HMD_LDAP_BINDCREDENTIALS || - process.env.HMD_LDAP_TOKENSECRET || - process.env.HMD_LDAP_SEARCHBASE || - process.env.HMD_LDAP_SEARCHFILTER || - process.env.HMD_LDAP_SEARCHATTRIBUTES || - process.env.HMD_LDAP_TLS_CA || - process.env.HMD_LDAP_PROVIDERNAME -) ? {} : false) -if (process.env.HMD_LDAP_URL) { ldap.url = process.env.HMD_LDAP_URL } -if (process.env.HMD_LDAP_BINDDN) { ldap.bindDn = process.env.HMD_LDAP_BINDDN } -if (process.env.HMD_LDAP_BINDCREDENTIALS) { ldap.bindCredentials = process.env.HMD_LDAP_BINDCREDENTIALS } -if (process.env.HMD_LDAP_TOKENSECRET) { ldap.tokenSecret = process.env.HMD_LDAP_TOKENSECRET } -if (process.env.HMD_LDAP_SEARCHBASE) { ldap.searchBase = process.env.HMD_LDAP_SEARCHBASE } -if (process.env.HMD_LDAP_SEARCHFILTER) { ldap.searchFilter = process.env.HMD_LDAP_SEARCHFILTER } -if (process.env.HMD_LDAP_SEARCHATTRIBUTES) { ldap.searchAttributes = process.env.HMD_LDAP_SEARCHATTRIBUTES } -if (process.env.HMD_LDAP_TLS_CA) { - var ca = { - ca: process.env.HMD_LDAP_TLS_CA.split(',') - } - ldap.tlsOptions = ldap.tlsOptions ? Object.assign(ldap.tlsOptions, ca) : ca - if (Array.isArray(ldap.tlsOptions.ca) && ldap.tlsOptions.ca.length > 0) { - var i, len, results - results = [] - for (i = 0, len = ldap.tlsOptions.ca.length; i < len; i++) { - results.push(fs.readFileSync(ldap.tlsOptions.ca[i], 'utf8')) - } - ldap.tlsOptions.ca = results - } -} -if (process.env.HMD_LDAP_PROVIDERNAME) { - ldap.providerName = process.env.HMD_LDAP_PROVIDERNAME -} -var imgur = handleDockerSecret('imgur_clientid') || process.env.HMD_IMGUR_CLIENTID || config.imgur || false -var email = process.env.HMD_EMAIL ? (process.env.HMD_EMAIL === 'true') : !!config.email -var allowemailregister = process.env.HMD_ALLOW_EMAIL_REGISTER ? (process.env.HMD_ALLOW_EMAIL_REGISTER === 'true') : ((typeof config.allowemailregister === 'boolean') ? config.allowemailregister : true) - -function getserverurl () { - var url = '' - if (domain) { - var protocol = protocolusessl ? 'https://' : 'http://' - url = protocol + domain - if (urladdport && ((usessl && port !== 443) || (!usessl && port !== 80))) { url += ':' + port } - } - if (urlpath) { url += '/' + urlpath } - return url -} - -var version = '0.5.1' -var minimumCompatibleVersion = '0.5.0' -var maintenance = true -var cwd = path.join(__dirname, '..') - -module.exports = { - raw: config, - handleDockerSecret: handleDockerSecret, - version: version, - minimumCompatibleVersion: minimumCompatibleVersion, - maintenance: maintenance, - domain: domain, - urlpath: urlpath, - debug: debug, - port: port, - alloworigin: alloworigin, - usessl: usessl, - serverurl: getserverurl(), - usecdn: usecdn, - allowanonymous: allowanonymous, - allowfreeurl: allowfreeurl, - defaultpermission: defaultpermission, - dburl: dburl, - db: db, - sslkeypath: path.join(cwd, sslkeypath), - sslcertpath: path.join(cwd, sslcertpath), - sslcapath: path.join(cwd, sslcapath), - dhparampath: path.join(cwd, dhparampath), - tmppath: path.join(cwd, tmppath), - defaultnotepath: path.join(cwd, defaultnotepath), - docspath: path.join(cwd, docspath), - indexpath: path.join(cwd, indexpath), - hackmdpath: path.join(cwd, hackmdpath), - errorpath: path.join(cwd, errorpath), - prettypath: path.join(cwd, prettypath), - slidepath: path.join(cwd, slidepath), - sessionname: sessionname, - sessionsecret: sessionsecret, - sessionlife: sessionlife, - staticcachetime: staticcachetime, - heartbeatinterval: heartbeatinterval, - heartbeattimeout: heartbeattimeout, - documentmaxlength: documentmaxlength, - facebook: facebook, - twitter: twitter, - github: github, - gitlab: gitlab, - dropbox: dropbox, - google: google, - ldap: ldap, - imgur: imgur, - email: email, - allowemailregister: allowemailregister, - imageUploadType: imageUploadType, - s3: s3, - s3bucket: s3bucket -} diff --git a/lib/config/default.js b/lib/config/default.js new file mode 100644 index 00000000..a14a4294 --- /dev/null +++ b/lib/config/default.js @@ -0,0 +1,92 @@ +'use strict' + +module.exports = { + domain: '', + urlpath: '', + port: 3000, + urladdport: false, + alloworigin: ['localhost'], + usessl: false, + protocolusessl: false, + usecdn: true, + allowanonymous: true, + allowfreeurl: false, + defaultpermission: 'editable', + dburl: '', + db: {}, + // ssl path + sslkeypath: '', + sslcertpath: '', + sslcapath: '', + dhparampath: '', + // other path + tmppath: './tmp', + defaultnotepath: './public/default.md', + docspath: './public/docs', + indexpath: './public/views/index.ejs', + hackmdpath: './public/views/hackmd.ejs', + errorpath: './public/views/error.ejs', + prettypath: './public/views/pretty.ejs', + slidepath: './public/views/slide.ejs', + // session + sessionname: 'connect.sid', + sessionsecret: 'secret', + sessionlife: 14 * 24 * 60 * 60 * 1000, // 14 days + staticcachetime: 1 * 24 * 60 * 60 * 1000, // 1 day + // socket.io + heartbeatinterval: 5000, + heartbeattimeout: 10000, + // document + documentmaxlength: 100000, + // image upload setting, available options are imgur/s3/filesystem + imageUploadType: 'filesystem', + imgur: { + clientID: undefined + }, + s3: { + accessKeyId: undefined, + secretAccessKey: undefined, + region: undefined + }, + s3bucket: undefined, + // authentication + facebook: { + clientID: undefined, + clientSecret: undefined + }, + twitter: { + consumerKey: undefined, + consumerSecret: undefined + }, + github: { + clientID: undefined, + clientSecret: undefined + }, + gitlab: { + baseURL: undefined, + clientID: undefined, + clientSecret: undefined, + scope: undefined + }, + dropbox: { + clientID: undefined, + clientSecret: undefined + }, + google: { + clientID: undefined, + clientSecret: undefined + }, + ldap: { + providerName: undefined, + url: undefined, + bindDn: undefined, + bindCredentials: undefined, + tokenSecret: undefined, + searchBase: undefined, + searchFilter: undefined, + searchAttributes: undefined, + tlsca: undefined + }, + email: true, + allowemailregister: true +} diff --git a/lib/config/defaultSSL.js b/lib/config/defaultSSL.js new file mode 100644 index 00000000..1f1d5590 --- /dev/null +++ b/lib/config/defaultSSL.js @@ -0,0 +1,17 @@ +'use strict' + +const fs = require('fs') + +function getFile (path) { + if (fs.existsSync(path)) { + return path + } + return undefined +} + +module.exports = { + sslkeypath: getFile('/run/secrets/key.pem'), + sslcertpath: getFile('/run/secrets/cert.pem'), + sslcapath: getFile('/run/secrets/ca.pem'), + dhparampath: getFile('/run/secrets/dhparam.pem') +} diff --git a/lib/config/dockerSecret.js b/lib/config/dockerSecret.js new file mode 100644 index 00000000..eea2fafd --- /dev/null +++ b/lib/config/dockerSecret.js @@ -0,0 +1,51 @@ +'use strict' + +const fs = require('fs') +const path = require('path') + +const basePath = path.resolve('/var/run/secrets/') + +function getSecret (secret) { + const filePath = path.join(basePath, secret) + if (fs.existsSync(filePath)) return fs.readFileSync(filePath) + return undefined +} + +if (fs.existsSync(basePath)) { + module.exports = { + sessionsecret: getSecret('sessionsecret'), + sslkeypath: getSecret('sslkeypath'), + sslcertpath: getSecret('sslcertpath'), + sslcapath: getSecret('sslcapath'), + dhparampath: getSecret('dhparampath'), + s3: { + accessKeyId: getSecret('s3_acccessKeyId'), + secretAccessKey: getSecret('s3_secretAccessKey') + }, + facebook: { + clientID: getSecret('facebook_clientID'), + clientSecret: getSecret('facebook_clientSecret') + }, + twitter: { + consumerKey: getSecret('twitter_consumerKey'), + consumerSecret: getSecret('twitter_consumerSecret') + }, + github: { + clientID: getSecret('github_clientID'), + clientSecret: getSecret('github_clientSecret') + }, + gitlab: { + clientID: getSecret('gitlab_clientID'), + clientSecret: getSecret('gitlab_clientSecret') + }, + dropbox: { + clientID: getSecret('dropbox_clientID'), + clientSecret: getSecret('dropbox_clientSecret') + }, + google: { + clientID: getSecret('google_clientID'), + clientSecret: getSecret('google_clientSecret') + }, + imgur: getSecret('imgur_clientid') + } +} diff --git a/lib/config/enum.js b/lib/config/enum.js new file mode 100644 index 00000000..07cdfcfe --- /dev/null +++ b/lib/config/enum.js @@ -0,0 +1,16 @@ +'use strict' + +exports.Environment = { + development: 'development', + production: 'production', + test: 'test' +} + +exports.Permission = { + freely: 'freely', + editable: 'editable', + limited: 'limited', + locked: 'locked', + protected: 'protected', + private: 'private' +} diff --git a/lib/config/environment.js b/lib/config/environment.js new file mode 100644 index 00000000..75381ffc --- /dev/null +++ b/lib/config/environment.js @@ -0,0 +1,67 @@ +'use strict' + +const {toBooleanConfig} = require('./utils') + +module.exports = { + domain: process.env.HMD_DOMAIN, + urlpath: process.env.HMD_URL_PATH, + port: process.env.HMD_PORT, + urladdport: process.env.HMD_URL_ADDPORT, + usessl: toBooleanConfig(process.env.HMD_USESSL), + protocolusessl: toBooleanConfig(process.env.HMD_PROTOCOL_USESSL), + alloworigin: process.env.HMD_ALLOW_ORIGIN ? process.env.HMD_ALLOW_ORIGIN.split(',') : undefined, + usecdn: toBooleanConfig(process.env.HMD_USECDN), + allowanonymous: toBooleanConfig(process.env.HMD_ALLOW_ANONYMOUS), + allowfreeurl: toBooleanConfig(process.env.HMD_ALLOW_FREEURL), + defaultpermission: process.env.HMD_DEFAULT_PERMISSION, + dburl: process.env.HMD_DB_URL, + imageUploadType: process.env.HMD_IMAGE_UPLOAD_TYPE, + imgur: { + clientID: process.env.HMD_IMGUR_CLIENTID + }, + s3: { + accessKeyId: process.env.HMD_S3_ACCESS_KEY_ID, + secretAccessKey: process.env.HMD_S3_SECRET_ACCESS_KEY, + region: process.env.HMD_S3_REGION + }, + s3bucket: process.env.HMD_S3_BUCKET, + facebook: { + clientID: process.env.HMD_FACEBOOK_CLIENTID, + clientSecret: process.env.HMD_FACEBOOK_CLIENTSECRET + }, + twitter: { + consumerKey: process.env.HMD_TWITTER_CONSUMERKEY, + consumerSecret: process.env.HMD_TWITTER_CONSUMERSECRET + }, + github: { + clientID: process.env.HMD_GITHUB_CLIENTID, + clientSecret: process.env.HMD_GITHUB_CLIENTSECRET + }, + gitlab: { + baseURL: process.env.HMD_GITLAB_BASEURL, + clientID: process.env.HMD_GITLAB_CLIENTID, + clientSecret: process.env.HMD_GITLAB_CLIENTSECRET, + scope: process.env.HMD_GITLAB_SCOPE + }, + dropbox: { + clientID: process.env.HMD_DROPBOX_CLIENTID, + clientSecret: process.env.HMD_DROPBOX_CLIENTSECRET + }, + google: { + clientID: process.env.HMD_GOOGLE_CLIENTID, + clientSecret: process.env.HMD_GOOGLE_CLIENTSECRET + }, + ldap: { + providerName: process.env.HMD_LDAP_PROVIDERNAME, + url: process.env.HMD_LDAP_URL, + bindDn: process.env.HMD_LDAP_BINDDN, + bindCredentials: process.env.HMD_LDAP_BINDCREDENTIALS, + tokenSecret: process.env.HMD_LDAP_TOKENSECRET, + searchBase: process.env.HMD_LDAP_SEARCHBASE, + searchFilter: process.env.HMD_LDAP_SEARCHFILTER, + searchAttributes: process.env.HMD_LDAP_SEARCHATTRIBUTES, + tlsca: process.env.HMD_LDAP_TLS_CA + }, + email: toBooleanConfig(process.env.HMD_EMAIL), + allowemailregister: toBooleanConfig(process.env.HMD_ALLOW_EMAIL_REGISTER) +} diff --git a/lib/config/index.js b/lib/config/index.js new file mode 100644 index 00000000..6bc9a419 --- /dev/null +++ b/lib/config/index.js @@ -0,0 +1,112 @@ +'use strict' + +const fs = require('fs') +const path = require('path') +const {merge} = require('lodash') +const deepFreeze = require('deep-freeze') +const {Environment, Permission} = require('./enum') + +const appRootPath = path.join(__dirname, '../../') +const env = process.env.NODE_ENV || Environment.development +const debugConfig = { + debug: (env === Environment.development) +} + +const packageConfig = { + version: '0.5.1', + minimumCompatibleVersion: '0.5.0' +} + +const configFilePath = path.join(__dirname, '../../config.json') +const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined + +let config = require('./default') +merge(config, require('./defaultSSL')) +merge(config, debugConfig) +merge(config, packageConfig) +merge(config, fileConfig) +merge(config, require('./oldEnvironment')) +merge(config, require('./environment')) +merge(config, require('./dockerSecret')) + +// load LDAP CA +if (config.ldap.tlsca) { + let ca = config.ldap.tlsca.split(',') + let caContent = [] + for (let i of ca) { + if (fs.existsSync(ca[i])) { + caContent.push(fs.readFileSync(ca[i], 'utf8')) + } + } + let tlsOptions = { + ca: caContent + } + config.ldap.tlsOptions = config.ldap.tlsOptions ? Object.assign(config.ldap.tlsOptions, tlsOptions) : tlsOptions +} + +// Permission +config.permission = Permission +if (!config.allowanonymous) { + delete config.permission.freely +} +if (!(config.defaultpermission in config.permission)) { + config.defaultpermission = config.permission.editable +} + +// cache result, cannot change config in runtime!!! +config.isStandardHTTPsPort = (function isStandardHTTPsPort () { + return config.usessl && config.port === 443 +})() +config.isStandardHTTPPort = (function isStandardHTTPPort () { + return !config.usessl && config.port === 80 +})() + +// cache serverURL +config.serverurl = (function getserverurl () { + var url = '' + if (config.domain) { + var protocol = config.protocolusessl ? 'https://' : 'http://' + url = protocol + config.domain + if (config.urladdport) { + if (!config.isStandardHTTPPort || !config.isStandardHTTPsPort) { + url += ':' + config.port + } + } + } + if (config.urlpath) { + url += '/' + config.urlpath + } + return url +})() + +config.Environment = Environment + +// auth method +config.isFacebookEnable = config.facebook.clientID && config.facebook.clientSecret +config.isGoogleEnable = config.google.clientID && config.google.clientSecret +config.isDropboxEnable = config.dropbox.clientID && config.dropbox.clientSecret +config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSecret +config.isEmailEnable = config.email +config.isGitHubEnable = config.github.clientID && config.github.clientSecret +config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret +config.isLDAPEnable = config.ldap.url + +// generate correct path +config.sslcapath = path.join(appRootPath, config.sslcapath) +config.sslcertpath = path.join(appRootPath, config.sslcertpath) +config.sslkeypath = path.join(appRootPath, config.sslkeypath) +config.dhparampath = path.join(appRootPath, config.dhparampath) + +config.tmppath = path.join(appRootPath, config.tmppath) +config.defaultnotepath = path.join(appRootPath, config.defaultnotepath) +config.docspath = path.join(appRootPath, config.docspath) +config.indexpath = path.join(appRootPath, config.indexpath) +config.hackmdpath = path.join(appRootPath, config.hackmdpath) +config.errorpath = path.join(appRootPath, config.errorpath) +config.prettypath = path.join(appRootPath, config.prettypath) +config.slidepath = path.join(appRootPath, config.slidepath) + +// maek config readonly +config = deepFreeze(config) + +module.exports = config diff --git a/lib/config/oldEnvironment.js b/lib/config/oldEnvironment.js new file mode 100644 index 00000000..a3b13cb9 --- /dev/null +++ b/lib/config/oldEnvironment.js @@ -0,0 +1,10 @@ +'use strict' + +const {toBooleanConfig} = require('./utils') + +module.exports = { + debug: toBooleanConfig(process.env.DEBUG), + dburl: process.env.DATABASE_URL, + urlpath: process.env.URL_PATH, + port: process.env.PORT +} diff --git a/lib/config/utils.js b/lib/config/utils.js new file mode 100644 index 00000000..11bbd8cb --- /dev/null +++ b/lib/config/utils.js @@ -0,0 +1,8 @@ +'use strict' + +exports.toBooleanConfig = function toBooleanConfig (configValue) { + if (configValue && typeof configValue === 'string') { + return (configValue === 'true') + } + return configValue +} diff --git a/lib/history.js b/lib/history.js index ffed3b96..f46ff49f 100644 --- a/lib/history.js +++ b/lib/history.js @@ -3,9 +3,9 @@ // external modules // core -var config = require('./config.js') -var logger = require('./logger.js') -var response = require('./response.js') +var config = require('./config') +var logger = require('./logger') +var response = require('./response') var models = require('./models') // public diff --git a/lib/logger.js b/lib/logger.js index 2111d69a..f8b3895c 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,23 +1,23 @@ 'use strict' -var winston = require('winston') -winston.emitErrs = true +const winston = require('winston') -var logger = new winston.Logger({ +class Logger extends winston.Logger { + // Implement stream.writable.write interface + write (chunk) { + this.info(chunk) + } +} + +module.exports = new Logger({ transports: [ new winston.transports.Console({ level: 'debug', handleExceptions: true, json: false, - colorize: true, + colorize: false, timestamp: true }) ], + emitErrs: true, exitOnError: false }) - -module.exports = logger -module.exports.stream = { - write: function (message, encoding) { - logger.info(message) - } -} diff --git a/lib/models/index.js b/lib/models/index.js index 95f900b6..0679a7fc 100644 --- a/lib/models/index.js +++ b/lib/models/index.js @@ -3,13 +3,14 @@ var fs = require('fs') var path = require('path') var Sequelize = require('sequelize') +const {cloneDeep} = require('lodash') // core -var config = require('../config.js') -var logger = require('../logger.js') +var config = require('../config') +var logger = require('../logger') -var dbconfig = config.db -dbconfig.logging = config.debug ? logger.info : false +var dbconfig = cloneDeep(config.db) +dbconfig.logger = config.debug ? logger.info : false var sequelize = null diff --git a/lib/models/note.js b/lib/models/note.js index f7c25bde..c0ef1374 100644 --- a/lib/models/note.js +++ b/lib/models/note.js @@ -15,11 +15,11 @@ var dmp = new DiffMatchPatch() var S = require('string') // core -var config = require('../config.js') -var logger = require('../logger.js') +var config = require('../config') +var logger = require('../logger') // ot -var ot = require('../ot/index.js') +var ot = require('../ot') // permission types var permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private'] diff --git a/lib/models/revision.js b/lib/models/revision.js index 581e7ea5..6f3a746f 100644 --- a/lib/models/revision.js +++ b/lib/models/revision.js @@ -7,8 +7,8 @@ var childProcess = require('child_process') var shortId = require('shortid') // core -var config = require('../config.js') -var logger = require('../logger.js') +var config = require('../config') +var logger = require('../logger') var dmpWorker = createDmpWorker() var dmpCallbackCache = {} diff --git a/lib/models/user.js b/lib/models/user.js index 042b0d2f..14c30bc3 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -5,8 +5,8 @@ var Sequelize = require('sequelize') var scrypt = require('scrypt') // core -var logger = require('../logger.js') -var letterAvatars = require('../letter-avatars.js') +var logger = require('../logger') +var letterAvatars = require('../letter-avatars') module.exports = function (sequelize, DataTypes) { var User = sequelize.define('User', { diff --git a/lib/realtime.js b/lib/realtime.js index 618fdad0..361bbf09 100644 --- a/lib/realtime.js +++ b/lib/realtime.js @@ -12,13 +12,13 @@ var chance = new Chance() var moment = require('moment') // core -var config = require('./config.js') -var logger = require('./logger.js') -var history = require('./history.js') +var config = require('./config') +var logger = require('./logger') +var history = require('./history') var models = require('./models') // ot -var ot = require('./ot/index.js') +var ot = require('./ot') // public var realtime = { @@ -28,7 +28,8 @@ var realtime = { secure: secure, connection: connection, getStatus: getStatus, - isReady: isReady + isReady: isReady, + maintenance: true } function onAuthorizeSuccess (data, accept) { @@ -699,7 +700,7 @@ function updateHistory (userId, note, time) { } function connection (socket) { - if (config.maintenance) return + if (realtime.maintenance) return parseNoteIdFromSocket(socket, function (err, noteId) { if (err) { return failConnection(500, err, socket) diff --git a/lib/response.js b/lib/response.js index 13a94bbf..a9abd1d4 100755 --- a/lib/response.js +++ b/lib/response.js @@ -10,8 +10,8 @@ var request = require('request') var moment = require('moment') // core -var config = require('./config.js') -var logger = require('./logger.js') +var config = require('./config') +var logger = require('./logger') var models = require('./models') // public @@ -59,14 +59,14 @@ function showIndex (req, res, next) { url: config.serverurl, useCDN: config.usecdn, allowAnonymous: config.allowanonymous, - facebook: config.facebook, - twitter: config.twitter, - github: config.github, - gitlab: config.gitlab, - dropbox: config.dropbox, - google: config.google, - ldap: config.ldap, - email: config.email, + facebook: config.isFacebookEnable, + twitter: config.isTwitterEnable, + github: config.isGitHubEnable, + gitlab: config.isGitLabEnable, + dropbox: config.isDropboxEnable, + google: config.isGoogleEnable, + ldap: config.isLDAPEnable, + email: config.isEmailEnable, allowemailregister: config.allowemailregister, signin: req.isAuthenticated(), infoMessage: req.flash('info'), @@ -89,14 +89,14 @@ function responseHackMD (res, note) { title: title, useCDN: config.usecdn, allowAnonymous: config.allowanonymous, - facebook: config.facebook, - twitter: config.twitter, - github: config.github, - gitlab: config.gitlab, - dropbox: config.dropbox, - google: config.google, - ldap: config.ldap, - email: config.email, + facebook: config.isFacebookEnable, + twitter: config.isTwitterEnable, + github: config.isGitHubEnable, + gitlab: config.isGitLabEnable, + dropbox: config.isDropboxEnable, + google: config.isGoogleEnable, + ldap: config.isLDAPEnable, + email: config.isEmailEnable, allowemailregister: config.allowemailregister }) } diff --git a/lib/web/auth/dropbox/index.js b/lib/web/auth/dropbox/index.js new file mode 100644 index 00000000..c03fbc57 --- /dev/null +++ b/lib/web/auth/dropbox/index.js @@ -0,0 +1,29 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const DropboxStrategy = require('passport-dropbox-oauth2').Strategy +const config = require('../../../config') +const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') + +let dropboxAuth = module.exports = Router() + +passport.use(new DropboxStrategy({ + apiVersion: '2', + clientID: config.dropbox.clientID, + clientSecret: config.dropbox.clientSecret, + callbackURL: config.serverurl + '/auth/dropbox/callback' +}, passportGeneralCallback)) + +dropboxAuth.get('/auth/dropbox', function (req, res, next) { + setReturnToFromReferer(req) + passport.authenticate('dropbox-oauth2')(req, res, next) +}) + +// dropbox auth callback +dropboxAuth.get('/auth/dropbox/callback', + passport.authenticate('dropbox-oauth2', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/' + }) +) diff --git a/lib/web/auth/email/index.js b/lib/web/auth/email/index.js new file mode 100644 index 00000000..760075f8 --- /dev/null +++ b/lib/web/auth/email/index.js @@ -0,0 +1,74 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const validator = require('validator') +const LocalStrategy = require('passport-local').Strategy +const config = require('../../../config') +const models = require('../../../models') +const logger = require('../../../logger') +const {setReturnToFromReferer} = require('../utils') +const {urlencodedParser} = require('../../utils') +const response = require('../../../response') + +let emailAuth = module.exports = Router() + +passport.use(new LocalStrategy({ + usernameField: 'email' +}, function (email, password, done) { + if (!validator.isEmail(email)) return done(null, false) + models.User.findOne({ + where: { + email: email + } + }).then(function (user) { + if (!user) return done(null, false) + if (!user.verifyPassword(password)) return done(null, false) + return done(null, user) + }).catch(function (err) { + logger.error(err) + return done(err) + }) +})) + +if (config.allowemailregister) { + emailAuth.post('/register', urlencodedParser, function (req, res, next) { + if (!req.body.email || !req.body.password) return response.errorBadRequest(res) + if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res) + models.User.findOrCreate({ + where: { + email: req.body.email + }, + defaults: { + password: req.body.password + } + }).spread(function (user, created) { + if (user) { + if (created) { + logger.debug('user registered: ' + user.id) + req.flash('info', "You've successfully registered, please signin.") + } else { + logger.debug('user found: ' + user.id) + req.flash('error', 'This email has been used, please try another one.') + } + return res.redirect(config.serverurl + '/') + } + req.flash('error', 'Failed to register your account, please try again.') + return res.redirect(config.serverurl + '/') + }).catch(function (err) { + logger.error('auth callback failed: ' + err) + return response.errorInternalError(res) + }) + }) +} + +emailAuth.post('/login', urlencodedParser, function (req, res, next) { + if (!req.body.email || !req.body.password) return response.errorBadRequest(res) + if (!validator.isEmail(req.body.email)) return response.errorBadRequest(res) + setReturnToFromReferer(req) + passport.authenticate('local', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/', + failureFlash: 'Invalid email or password.' + })(req, res, next) +}) diff --git a/lib/web/auth/facebook/index.js b/lib/web/auth/facebook/index.js new file mode 100644 index 00000000..0e5474d8 --- /dev/null +++ b/lib/web/auth/facebook/index.js @@ -0,0 +1,29 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const FacebookStrategy = require('passport-facebook').Strategy + +const config = require('../../../config') +const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') + +let facebookAuth = module.exports = Router() + +passport.use(new FacebookStrategy({ + clientID: config.facebook.clientID, + clientSecret: config.facebook.clientSecret, + callbackURL: config.serverurl + '/auth/facebook/callback' +}, passportGeneralCallback)) + +facebookAuth.get('/auth/facebook', function (req, res, next) { + setReturnToFromReferer(req) + passport.authenticate('facebook')(req, res, next) +}) + +// facebook auth callback +facebookAuth.get('/auth/facebook/callback', + passport.authenticate('facebook', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/' + }) +) diff --git a/lib/web/auth/github/index.js b/lib/web/auth/github/index.js new file mode 100644 index 00000000..2a26669c --- /dev/null +++ b/lib/web/auth/github/index.js @@ -0,0 +1,28 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const GithubStrategy = require('passport-github').Strategy +const config = require('../../../config') +const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') + +let githubAuth = module.exports = Router() + +passport.use(new GithubStrategy({ + clientID: config.github.clientID, + clientSecret: config.github.clientSecret, + callbackURL: config.serverurl + '/auth/github/callback' +}, passportGeneralCallback)) + +githubAuth.get('/auth/github', function (req, res, next) { + setReturnToFromReferer(req) + passport.authenticate('github')(req, res, next) +}) + +// github auth callback +githubAuth.get('/auth/github/callback', + passport.authenticate('github', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/' + }) +) diff --git a/lib/web/auth/gitlab/index.js b/lib/web/auth/gitlab/index.js new file mode 100644 index 00000000..51de1602 --- /dev/null +++ b/lib/web/auth/gitlab/index.js @@ -0,0 +1,36 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const GitlabStrategy = require('passport-gitlab2').Strategy +const config = require('../../../config') +const response = require('../../../response') +const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') + +let gitlabAuth = module.exports = Router() + +passport.use(new GitlabStrategy({ + baseURL: config.gitlab.baseURL, + clientID: config.gitlab.clientID, + clientSecret: config.gitlab.clientSecret, + scope: config.gitlab.scope, + callbackURL: config.serverurl + '/auth/gitlab/callback' +}, passportGeneralCallback)) + +gitlabAuth.get('/auth/gitlab', function (req, res, next) { + setReturnToFromReferer(req) + passport.authenticate('gitlab')(req, res, next) +}) + +// gitlab auth callback +gitlabAuth.get('/auth/gitlab/callback', + passport.authenticate('gitlab', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/' + }) +) + +if (!config.gitlab.scope || config.gitlab.scope === 'api') { + // gitlab callback actions + gitlabAuth.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions) +} diff --git a/lib/web/auth/google/index.js b/lib/web/auth/google/index.js new file mode 100644 index 00000000..bf2a260f --- /dev/null +++ b/lib/web/auth/google/index.js @@ -0,0 +1,27 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +var GoogleStrategy = require('passport-google-oauth20').Strategy +const config = require('../../../config') +const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') + +let facebookAuth = module.exports = Router() + +passport.use(new GoogleStrategy({ + clientID: config.google.clientID, + clientSecret: config.google.clientSecret, + callbackURL: config.serverurl + '/auth/google/callback' +}, passportGeneralCallback)) + +facebookAuth.get('/auth/google', function (req, res, next) { + setReturnToFromReferer(req) + passport.authenticate('google', { scope: ['profile'] })(req, res, next) +}) + // google auth callback +facebookAuth.get('/auth/google/callback', + passport.authenticate('google', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/' + }) +) diff --git a/lib/web/auth/index.js b/lib/web/auth/index.js new file mode 100644 index 00000000..b5ca8434 --- /dev/null +++ b/lib/web/auth/index.js @@ -0,0 +1,48 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') + +const config = require('../../config') +const logger = require('../../logger') +const models = require('../../models') + +const authRouter = module.exports = Router() + +// serialize and deserialize +passport.serializeUser(function (user, done) { + logger.info('serializeUser: ' + user.id) + return done(null, user.id) +}) + +passport.deserializeUser(function (id, done) { + models.User.findOne({ + where: { + id: id + } + }).then(function (user) { + logger.info('deserializeUser: ' + user.id) + return done(null, user) + }).catch(function (err) { + logger.error(err) + return done(err, null) + }) +}) + +if (config.isFacebookEnable) authRouter.use(require('./facebook')) +if (config.isTwitterEnable) authRouter.use(require('./twitter')) +if (config.isGitHubEnable) authRouter.use(require('./github')) +if (config.isGitLabEnable) authRouter.use(require('./gitlab')) +if (config.isDropboxEnable) authRouter.use(require('./dropbox')) +if (config.isGoogleEnable) authRouter.use(require('./google')) +if (config.isLDAPEnable) authRouter.use(require('./ldap')) +if (config.isEmailEnable) authRouter.use(require('./email')) + +// logout +authRouter.get('/logout', function (req, res) { + if (config.debug && req.isAuthenticated()) { + logger.debug('user logout: ' + req.user.id) + } + req.logout() + res.redirect(config.serverurl + '/') +}) diff --git a/lib/web/auth/ldap/index.js b/lib/web/auth/ldap/index.js new file mode 100644 index 00000000..766c5cbc --- /dev/null +++ b/lib/web/auth/ldap/index.js @@ -0,0 +1,74 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const LDAPStrategy = require('passport-ldapauth') +const config = require('../../../config') +const models = require('../../../models') +const logger = require('../../../logger') +const {setReturnToFromReferer} = require('../utils') +const {urlencodedParser} = require('../../utils') +const response = require('../../../response') + +let ldapAuth = module.exports = Router() + +passport.use(new LDAPStrategy({ + server: { + url: config.ldap.url || null, + bindDn: config.ldap.bindDn || null, + bindCredentials: config.ldap.bindCredentials || null, + searchBase: config.ldap.searchBase || null, + searchFilter: config.ldap.searchFilter || null, + searchAttributes: config.ldap.searchAttributes || null, + tlsOptions: config.ldap.tlsOptions || null + } +}, function (user, done) { + var profile = { + id: 'LDAP-' + user.uidNumber, + username: user.uid, + displayName: user.displayName, + emails: user.mail ? [user.mail] : [], + avatarUrl: null, + profileUrl: null, + provider: 'ldap' + } + var stringifiedProfile = JSON.stringify(profile) + models.User.findOrCreate({ + where: { + profileid: profile.id.toString() + }, + defaults: { + profile: stringifiedProfile + } + }).spread(function (user, created) { + if (user) { + var needSave = false + if (user.profile !== stringifiedProfile) { + user.profile = stringifiedProfile + needSave = true + } + if (needSave) { + user.save().then(function () { + if (config.debug) { logger.debug('user login: ' + user.id) } + return done(null, user) + }) + } else { + if (config.debug) { logger.debug('user login: ' + user.id) } + return done(null, user) + } + } + }).catch(function (err) { + logger.error('ldap auth failed: ' + err) + return done(err, null) + }) +})) + +ldapAuth.post('/auth/ldap', urlencodedParser, function (req, res, next) { + if (!req.body.username || !req.body.password) return response.errorBadRequest(res) + setReturnToFromReferer(req) + passport.authenticate('ldapauth', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/', + failureFlash: true + })(req, res, next) +}) diff --git a/lib/web/auth/twitter/index.js b/lib/web/auth/twitter/index.js new file mode 100644 index 00000000..5429522d --- /dev/null +++ b/lib/web/auth/twitter/index.js @@ -0,0 +1,29 @@ +'use strict' + +const Router = require('express').Router +const passport = require('passport') +const TwitterStrategy = require('passport-twitter').Strategy + +const config = require('../../../config') +const {setReturnToFromReferer, passportGeneralCallback} = require('../utils') + +let twitterAuth = module.exports = Router() + +passport.use(new TwitterStrategy({ + consumerKey: config.twitter.consumerKey, + consumerSecret: config.twitter.consumerSecret, + callbackURL: config.serverurl + '/auth/twitter/callback' +}, passportGeneralCallback)) + +twitterAuth.get('/auth/twitter', function (req, res, next) { + setReturnToFromReferer(req) + passport.authenticate('twitter')(req, res, next) +}) + +// twitter auth callback +twitterAuth.get('/auth/twitter/callback', + passport.authenticate('twitter', { + successReturnToOrRedirect: config.serverurl + '/', + failureRedirect: config.serverurl + '/' + }) +) diff --git a/lib/web/auth/utils.js b/lib/web/auth/utils.js new file mode 100644 index 00000000..ff7a1237 --- /dev/null +++ b/lib/web/auth/utils.js @@ -0,0 +1,53 @@ +'use strict' + +const models = require('../../models') +const config = require('../../config') +const logger = require('../../logger') + +exports.setReturnToFromReferer = function setReturnToFromReferer (req) { + var referer = req.get('referer') + if (!req.session) req.session = {} + req.session.returnTo = referer +} + +exports.passportGeneralCallback = function callback (accessToken, refreshToken, profile, done) { + var stringifiedProfile = JSON.stringify(profile) + models.User.findOrCreate({ + where: { + profileid: profile.id.toString() + }, + defaults: { + profile: stringifiedProfile, + accessToken: accessToken, + refreshToken: refreshToken + } + }).spread(function (user, created) { + if (user) { + var needSave = false + if (user.profile !== stringifiedProfile) { + user.profile = stringifiedProfile + needSave = true + } + if (user.accessToken !== accessToken) { + user.accessToken = accessToken + needSave = true + } + if (user.refreshToken !== refreshToken) { + user.refreshToken = refreshToken + needSave = true + } + if (needSave) { + user.save().then(function () { + if (config.debug) { logger.info('user login: ' + user.id) } + return done(null, user) + }) + } else { + if (config.debug) { logger.info('user login: ' + user.id) } + return done(null, user) + } + } + }).catch(function (err) { + logger.error('auth callback failed: ' + err) + return done(err, null) + }) +} diff --git a/lib/web/baseRouter.js b/lib/web/baseRouter.js new file mode 100644 index 00000000..b918ce75 --- /dev/null +++ b/lib/web/baseRouter.js @@ -0,0 +1,22 @@ +'use strict' + +const Router = require('express').Router + +const response = require('../response') + +const baseRouter = module.exports = Router() + +// get index +baseRouter.get('/', response.showIndex) +// get 403 forbidden +baseRouter.get('/403', function (req, res) { + response.errorForbidden(res) +}) +// get 404 not found +baseRouter.get('/404', function (req, res) { + response.errorNotFound(res) +}) +// get 500 internal error +baseRouter.get('/500', function (req, res) { + response.errorInternalError(res) +}) diff --git a/lib/web/historyRouter.js b/lib/web/historyRouter.js new file mode 100644 index 00000000..1b22c232 --- /dev/null +++ b/lib/web/historyRouter.js @@ -0,0 +1,18 @@ +'use strict' + +const Router = require('express').Router + +const {urlencodedParser} = require('./utils') +const history = require('../history') +const historyRouter = module.exports = Router() + +// get history +historyRouter.get('/history', history.historyGet) +// post history +historyRouter.post('/history', urlencodedParser, history.historyPost) +// post history by note id +historyRouter.post('/history/:noteId', urlencodedParser, history.historyPost) +// delete history +historyRouter.delete('/history', history.historyDelete) +// delete history by note id +historyRouter.delete('/history/:noteId', history.historyDelete) diff --git a/lib/web/imageRouter.js b/lib/web/imageRouter.js new file mode 100644 index 00000000..592a497c --- /dev/null +++ b/lib/web/imageRouter.js @@ -0,0 +1,95 @@ +'use strict' +var fs = require('fs') +var url = require('url') +var path = require('path') + +const Router = require('express').Router +const formidable = require('formidable') +var imgur = require('imgur') + +const config = require('../config') +const logger = require('../logger') +const response = require('../response') + +const imageRouter = module.exports = Router() + +// upload image +imageRouter.post('/uploadimage', function (req, res) { + var form = new formidable.IncomingForm() + + form.keepExtensions = true + + if (config.imageUploadType === 'filesystem') { + form.uploadDir = 'public/uploads' + } + + form.parse(req, function (err, fields, files) { + if (err || !files.image || !files.image.path) { + response.errorForbidden(res) + } else { + if (config.debug) { logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)) } + + try { + switch (config.imageUploadType) { + case 'filesystem': + res.send({ + link: url.resolve(config.serverurl + '/', files.image.path.match(/^public\/(.+$)/)[1]) + }) + + break + + case 's3': + var AWS = require('aws-sdk') + var awsConfig = new AWS.Config(config.s3) + var s3 = new AWS.S3(awsConfig) + const {getImageMimeType} = require('../utils') + fs.readFile(files.image.path, function (err, buffer) { + if (err) { + logger.error(err) + res.status(500).end('upload image error') + return + } + var params = { + Bucket: config.s3bucket, + Key: path.join('uploads', path.basename(files.image.path)), + Body: buffer + } + + var mimeType = getImageMimeType(files.image.path) + if (mimeType) { params.ContentType = mimeType } + + s3.putObject(params, function (err, data) { + if (err) { + logger.error(err) + res.status(500).end('upload image error') + return + } + res.send({ + link: `https://s3-${config.s3.region}.amazonaws.com/${config.s3bucket}/${params.Key}` + }) + }) + }) + break + case 'imgur': + default: + imgur.setClientId(config.imgur.clientID) + imgur.uploadFile(files.image.path) + .then(function (json) { + if (config.debug) { logger.info('SERVER uploadimage success: ' + JSON.stringify(json)) } + res.send({ + link: json.data.link.replace(/^http:\/\//i, 'https://') + }) + }) + .catch(function (err) { + logger.error(err) + return res.status(500).end('upload image error') + }) + break + } + } catch (err) { + logger.error(err) + return res.status(500).end('upload image error') + } + } + }) +}) diff --git a/lib/web/middleware/checkURIValid.js b/lib/web/middleware/checkURIValid.js new file mode 100644 index 00000000..88065e79 --- /dev/null +++ b/lib/web/middleware/checkURIValid.js @@ -0,0 +1,14 @@ +'use strict' + +const logger = require('../../logger') +const response = require('../../response') + +module.exports = function (req, res, next) { + try { + decodeURIComponent(req.path) + } catch (err) { + logger.error(err) + return response.errorBadRequest(res) + } + next() +} diff --git a/lib/web/middleware/redirectWithoutTrailingSlashes.js b/lib/web/middleware/redirectWithoutTrailingSlashes.js new file mode 100644 index 00000000..fbaba617 --- /dev/null +++ b/lib/web/middleware/redirectWithoutTrailingSlashes.js @@ -0,0 +1,17 @@ +'use strict' + +const config = require('../../config') + +module.exports = function (req, res, next) { + if (req.method === 'GET' && req.path.substr(-1) === '/' && req.path.length > 1) { + const queryString = req.url.slice(req.path.length) + const urlPath = req.path.slice(0, -1) + let serverURL = config.serverurl + if (config.urlpath) { + serverURL = serverURL.slice(0, -(config.urlpath.length + 1)) + } + res.redirect(301, serverURL + urlPath + queryString) + } else { + next() + } +} diff --git a/lib/web/middleware/tooBusy.js b/lib/web/middleware/tooBusy.js new file mode 100644 index 00000000..f1b72330 --- /dev/null +++ b/lib/web/middleware/tooBusy.js @@ -0,0 +1,13 @@ +'use strict' + +const toobusy = require('toobusy-js') + +const response = require('../../response') + +module.exports = function (req, res, next) { + if (toobusy()) { + response.errorServiceUnavailable(res) + } else { + next() + } +} diff --git a/lib/web/noteRouter.js b/lib/web/noteRouter.js new file mode 100644 index 00000000..007c02c2 --- /dev/null +++ b/lib/web/noteRouter.js @@ -0,0 +1,24 @@ +'use strict' + +const Router = require('express').Router + +const response = require('../response') + +const noteRouter = module.exports = Router() + +// get new note +noteRouter.get('/new', response.newNote) +// get publish note +noteRouter.get('/s/:shortid', response.showPublishNote) +// publish note actions +noteRouter.get('/s/:shortid/:action', response.publishNoteActions) +// get publish slide +noteRouter.get('/p/:shortid', response.showPublishSlide) +// publish slide actions +noteRouter.get('/p/:shortid/:action', response.publishSlideActions) +// get note by id +noteRouter.get('/:noteId', response.showNote) +// note actions +noteRouter.get('/:noteId/:action', response.noteActions) +// note actions with action id +noteRouter.get('/:noteId/:action/:actionId', response.noteActions) diff --git a/lib/web/statusRouter.js b/lib/web/statusRouter.js new file mode 100644 index 00000000..aa3a9b79 --- /dev/null +++ b/lib/web/statusRouter.js @@ -0,0 +1,92 @@ +'use strict' + +const Router = require('express').Router + +const response = require('../response') +const realtime = require('../realtime') +const config = require('../config') +const models = require('../models') +const logger = require('../logger') + +const {urlencodedParser} = require('./utils') + +const statusRouter = module.exports = Router() + +// get status +statusRouter.get('/status', function (req, res, next) { + realtime.getStatus(function (data) { + res.set({ + 'Cache-Control': 'private', // only cache by client + 'X-Robots-Tag': 'noindex, nofollow', // prevent crawling + 'HackMD-Version': config.version + }) + res.send(data) + }) +}) +// get status +statusRouter.get('/temp', function (req, res) { + var host = req.get('host') + if (config.alloworigin.indexOf(host) === -1) { + response.errorForbidden(res) + } else { + var tempid = req.query.tempid + if (!tempid) { + response.errorForbidden(res) + } else { + models.Temp.findOne({ + where: { + id: tempid + } + }).then(function (temp) { + if (!temp) { + response.errorNotFound(res) + } else { + res.header('Access-Control-Allow-Origin', '*') + res.send({ + temp: temp.data + }) + temp.destroy().catch(function (err) { + if (err) { + logger.error('remove temp failed: ' + err) + } + }) + } + }).catch(function (err) { + logger.error(err) + return response.errorInternalError(res) + }) + } + } +}) +// post status +statusRouter.post('/temp', urlencodedParser, function (req, res) { + var host = req.get('host') + if (config.alloworigin.indexOf(host) === -1) { + response.errorForbidden(res) + } else { + var data = req.body.data + if (!data) { + response.errorForbidden(res) + } else { + if (config.debug) { + logger.info('SERVER received temp from [' + host + ']: ' + req.body.data) + } + models.Temp.create({ + data: data + }).then(function (temp) { + if (temp) { + res.header('Access-Control-Allow-Origin', '*') + res.send({ + status: 'ok', + id: temp.id + }) + } else { + response.errorInternalError(res) + } + }).catch(function (err) { + logger.error(err) + return response.errorInternalError(res) + }) + } + } +}) diff --git a/lib/web/userRouter.js b/lib/web/userRouter.js new file mode 100644 index 00000000..ecfbaf8b --- /dev/null +++ b/lib/web/userRouter.js @@ -0,0 +1,36 @@ +'use strict' + +const Router = require('express').Router + +const response = require('../response') +const models = require('../models') +const logger = require('../logger') + +const UserRouter = module.exports = Router() + +// get me info +UserRouter.get('/me', function (req, res) { + if (req.isAuthenticated()) { + models.User.findOne({ + where: { + id: req.user.id + } + }).then(function (user) { + if (!user) { return response.errorNotFound(res) } + var profile = models.User.getProfile(user) + res.send({ + status: 'ok', + id: req.user.id, + name: profile.name, + photo: profile.photo + }) + }).catch(function (err) { + logger.error('read me failed: ' + err) + return response.errorInternalError(res) + }) + } else { + res.send({ + status: 'forbidden' + }) + } +}) diff --git a/lib/web/utils.js b/lib/web/utils.js new file mode 100644 index 00000000..c9016523 --- /dev/null +++ b/lib/web/utils.js @@ -0,0 +1,9 @@ +'use strict' + +const bodyParser = require('body-parser') + +// create application/x-www-form-urlencoded parser +exports.urlencodedParser = bodyParser.urlencoded({ + extended: false, + limit: 1024 * 1024 * 10 // 10 mb +}) diff --git a/lib/workers/dmpWorker.js b/lib/workers/dmpWorker.js index b0ed0f43..60db0a12 100644 --- a/lib/workers/dmpWorker.js +++ b/lib/workers/dmpWorker.js @@ -4,8 +4,8 @@ var DiffMatchPatch = require('diff-match-patch') var dmp = new DiffMatchPatch() // core -var config = require('../config.js') -var logger = require('../logger.js') +var config = require('../config') +var logger = require('../logger') process.on('message', function (data) { if (!data || !data.msg || !data.cacheKey) { diff --git a/package.json b/package.json index 3637fd58..7e38fda8 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "connect-session-sequelize": "^4.1.0", "cookie": "0.3.1", "cookie-parser": "1.4.3", + "deep-freeze": "^0.0.1", "diff-match-patch": "git+https://github.com/hackmdio/diff-match-patch.git", "ejs": "^2.5.5", "emojify.js": "~1.1.0", diff --git a/public/js/extra.js b/public/js/extra.js index 6febd880..bf388139 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -750,7 +750,7 @@ export function generateToc (id) { 'targetId': id, 'process': getHeaderContent }) - /* eslint-enable no-unsed-vars */ + /* eslint-enable no-unused-vars */ if (target.text() === 'undefined') { target.html('') } const tocMenu = $('<div class="toc-menu"></div') const toggle = $('<a class="expand-toggle" href="#">Expand all</a>') @@ -888,7 +888,7 @@ export function renderTOC (view) { const target = $(`#${id}`) target.html('') /* eslint-disable no-unused-vars */ - var toc = new window.Toc('doc', { + let TOC = new window.Toc('doc', { 'level': 3, 'top': -1, 'class': 'toc', @@ -39,10 +39,6 @@ acorn-object-spread@^1.0.0: dependencies: acorn "^3.1.0" -acorn@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - acorn@^2.0.1: version "2.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" @@ -55,6 +51,10 @@ acorn@^4.0.4: version "4.0.11" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" +acorn@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -64,8 +64,8 @@ ajv-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" ajv@^4.7.0, ajv@^4.9.1: - version "4.11.5" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.5.tgz#b6ee74657b993a01dce44b7944d56f485828d5bd" + version "4.11.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.6.tgz#947e93049790942b2a2d60a8289b28924d39f987" dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" @@ -197,8 +197,8 @@ array-unique@^0.2.1: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" array.prototype.find@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.3.tgz#08c3ec33e32ec4bab362a2958e686ae92f59271d" + version "2.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" dependencies: define-properties "^1.1.2" es-abstract "^1.7.0" @@ -250,8 +250,8 @@ async@^1.3.0, async@^1.4.0, async@^1.5.0, async@^1.5.2, async@~1.5.2: resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" async@^2.1.4: - version "2.1.5" - resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + version "2.3.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.3.0.tgz#1013d1051047dd320fe24e494d5c66ecaf6147d9" dependencies: lodash "^4.14.0" @@ -283,18 +283,18 @@ autoprefixer@^6.3.1: postcss-value-parser "^3.2.3" aws-sdk@^2.7.20: - version "2.32.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.32.0.tgz#9fe278b2ec1b12fddd1afeb74439581cdc2e5990" + version "2.41.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.41.0.tgz#930c15ae7ef4532bb8912c4c36a18b0023fd74c2" dependencies: buffer "4.9.1" crypto-browserify "1.0.9" jmespath "0.15.0" querystring "0.2.0" - sax "1.1.5" + sax "1.2.1" url "0.10.3" - uuid "3.0.0" - xml2js "0.4.15" - xmlbuilder "2.6.2" + uuid "3.0.1" + xml2js "0.4.17" + xmlbuilder "4.2.1" aws-sign2@~0.6.0: version "0.6.0" @@ -305,12 +305,12 @@ aws4@^1.2.1: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" babel-cli@^6.18.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.24.0.tgz#a05ffd210dca0c288a26d5319c5ac8669a265ad0" + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.24.1.tgz#207cd705bba61489b2ea41b5312341cf6aca2283" dependencies: - babel-core "^6.24.0" + babel-core "^6.24.1" babel-polyfill "^6.23.0" - babel-register "^6.24.0" + babel-register "^6.24.1" babel-runtime "^6.22.0" commander "^2.8.1" convert-source-map "^1.1.0" @@ -333,19 +333,19 @@ babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.0" -babel-core@^6.21.0, babel-core@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.0.tgz#8f36a0a77f5c155aed6f920b844d23ba56742a02" +babel-core@^6.21.0, babel-core@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" dependencies: babel-code-frame "^6.22.0" - babel-generator "^6.24.0" - babel-helpers "^6.23.0" + babel-generator "^6.24.1" + babel-helpers "^6.24.1" babel-messages "^6.23.0" - babel-register "^6.24.0" + babel-register "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.1" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" babylon "^6.11.0" convert-source-map "^1.1.0" debug "^2.1.1" @@ -357,93 +357,93 @@ babel-core@^6.21.0, babel-core@^6.24.0: slash "^1.0.0" source-map "^0.5.0" -babel-generator@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.0.tgz#eba270a8cc4ce6e09a61be43465d7c62c1f87c56" +babel-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497" dependencies: babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" detect-indent "^4.0.0" jsesc "^1.3.0" lodash "^4.2.0" source-map "^0.5.0" trim-right "^1.0.1" -babel-helper-call-delegate@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef" +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" dependencies: - babel-helper-hoist-variables "^6.22.0" + babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" - babel-traverse "^6.22.0" - babel-types "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-define-map@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.23.0.tgz#1444f960c9691d69a2ced6a205315f8fd00804e7" +babel-helper-define-map@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080" dependencies: - babel-helper-function-name "^6.23.0" + babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" lodash "^4.2.0" -babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.23.0.tgz#25742d67175c8903dbe4b6cb9d9e1fcb8dcf23a6" +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" dependencies: - babel-helper-get-function-arity "^6.22.0" + babel-helper-get-function-arity "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-get-function-arity@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce" +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-helper-hoist-variables@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72" +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-helper-optimise-call-expression@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.23.0.tgz#f3ee7eed355b4282138b33d02b78369e470622f5" +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" dependencies: babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" -babel-helper-regex@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz#79f532be1647b1f0ee3474b5f5c3da58001d247d" +babel-helper-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" lodash "^4.2.0" -babel-helper-replace-supers@^6.22.0, babel-helper-replace-supers@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.23.0.tgz#eeaf8ad9b58ec4337ca94223bacdca1f8d9b4bfd" +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" dependencies: - babel-helper-optimise-call-expression "^6.23.0" + babel-helper-optimise-call-expression "^6.24.1" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helpers@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.23.0.tgz#4f8f2e092d0b6a8808a4bde79c27f1e2ecf0d992" +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" dependencies: babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" babel-loader@^6.2.10: version "6.4.1" @@ -478,36 +478,36 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-block-scoping@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.23.0.tgz#e48895cf0b375be148cd7c8879b422707a053b51" +babel-plugin-transform-es2015-block-scoping@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576" dependencies: babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" lodash "^4.2.0" -babel-plugin-transform-es2015-classes@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.23.0.tgz#49b53f326202a2fd1b3bbaa5e2edd8a4f78643c1" +babel-plugin-transform-es2015-classes@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" dependencies: - babel-helper-define-map "^6.23.0" - babel-helper-function-name "^6.23.0" - babel-helper-optimise-call-expression "^6.23.0" - babel-helper-replace-supers "^6.23.0" + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-computed-properties@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz#7c383e9629bba4820c11b0425bdd6290f7f057e7" +babel-plugin-transform-es2015-computed-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" dependencies: babel-runtime "^6.22.0" - babel-template "^6.22.0" + babel-template "^6.24.1" babel-plugin-transform-es2015-destructuring@^6.22.0: version "6.23.0" @@ -515,12 +515,12 @@ babel-plugin-transform-es2015-destructuring@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-duplicate-keys@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.22.0.tgz#672397031c21610d72dd2bbb0ba9fb6277e1c36b" +babel-plugin-transform-es2015-duplicate-keys@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-for-of@^6.22.0: version "6.23.0" @@ -528,13 +528,13 @@ babel-plugin-transform-es2015-for-of@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-function-name@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz#f5fcc8b09093f9a23c76ac3d9e392c3ec4b77104" +babel-plugin-transform-es2015-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" dependencies: - babel-helper-function-name "^6.22.0" + babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-literals@^6.22.0: version "6.22.0" @@ -542,63 +542,63 @@ babel-plugin-transform-es2015-literals@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-modules-amd@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.0.tgz#a1911fb9b7ec7e05a43a63c5995007557bcf6a2e" +babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.24.0" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.22.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-modules-commonjs@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.0.tgz#e921aefb72c2cc26cb03d107626156413222134f" +babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe" dependencies: - babel-plugin-transform-strict-mode "^6.22.0" + babel-plugin-transform-strict-mode "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-modules-systemjs@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.23.0.tgz#ae3469227ffac39b0310d90fec73bfdc4f6317b0" +babel-plugin-transform-es2015-modules-systemjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" dependencies: - babel-helper-hoist-variables "^6.22.0" + babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-modules-umd@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.0.tgz#fd5fa63521cae8d273927c3958afd7c067733450" +babel-plugin-transform-es2015-modules-umd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" dependencies: - babel-plugin-transform-es2015-modules-amd "^6.24.0" + babel-plugin-transform-es2015-modules-amd "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-object-super@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz#daa60e114a042ea769dd53fe528fc82311eb98fc" +babel-plugin-transform-es2015-object-super@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" dependencies: - babel-helper-replace-supers "^6.22.0" + babel-helper-replace-supers "^6.24.1" babel-runtime "^6.22.0" -babel-plugin-transform-es2015-parameters@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.23.0.tgz#3a2aabb70c8af945d5ce386f1a4250625a83ae3b" +babel-plugin-transform-es2015-parameters@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" dependencies: - babel-helper-call-delegate "^6.22.0" - babel-helper-get-function-arity "^6.22.0" + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-shorthand-properties@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz#8ba776e0affaa60bff21e921403b8a652a2ff723" +babel-plugin-transform-es2015-shorthand-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-spread@^6.22.0: version "6.22.0" @@ -606,13 +606,13 @@ babel-plugin-transform-es2015-spread@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-sticky-regex@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz#ab316829e866ee3f4b9eb96939757d19a5bc4593" +babel-plugin-transform-es2015-sticky-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" dependencies: - babel-helper-regex "^6.22.0" + babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-template-literals@^6.22.0: version "6.22.0" @@ -626,19 +626,19 @@ babel-plugin-transform-es2015-typeof-symbol@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-unicode-regex@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz#8d9cc27e7ee1decfe65454fb986452a04a613d20" +babel-plugin-transform-es2015-unicode-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" dependencies: - babel-helper-regex "^6.22.0" + babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" regexpu-core "^2.0.0" -babel-plugin-transform-regenerator@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.22.0.tgz#65740593a319c44522157538d690b84094617ea6" +babel-plugin-transform-regenerator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418" dependencies: - regenerator-transform "0.9.8" + regenerator-transform "0.9.11" babel-plugin-transform-runtime@^6.15.0: version "6.23.0" @@ -646,12 +646,12 @@ babel-plugin-transform-runtime@^6.15.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-strict-mode@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz#e008df01340fdc87e959da65991b7e05970c8c7c" +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-polyfill@^6.22.0, babel-polyfill@^6.23.0: version "6.23.0" @@ -662,39 +662,39 @@ babel-polyfill@^6.22.0, babel-polyfill@^6.23.0: regenerator-runtime "^0.10.0" babel-preset-es2015@^6.18.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.0.tgz#c162d68b1932696e036cd3110dc1ccd303d2673a" + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" dependencies: babel-plugin-check-es2015-constants "^6.22.0" babel-plugin-transform-es2015-arrow-functions "^6.22.0" babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.22.0" - babel-plugin-transform-es2015-classes "^6.22.0" - babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.24.1" + babel-plugin-transform-es2015-classes "^6.24.1" + babel-plugin-transform-es2015-computed-properties "^6.24.1" babel-plugin-transform-es2015-destructuring "^6.22.0" - babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-duplicate-keys "^6.24.1" babel-plugin-transform-es2015-for-of "^6.22.0" - babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-function-name "^6.24.1" babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.24.0" - babel-plugin-transform-es2015-modules-commonjs "^6.24.0" - babel-plugin-transform-es2015-modules-systemjs "^6.22.0" - babel-plugin-transform-es2015-modules-umd "^6.24.0" - babel-plugin-transform-es2015-object-super "^6.22.0" - babel-plugin-transform-es2015-parameters "^6.22.0" - babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-plugin-transform-es2015-modules-systemjs "^6.24.1" + babel-plugin-transform-es2015-modules-umd "^6.24.1" + babel-plugin-transform-es2015-object-super "^6.24.1" + babel-plugin-transform-es2015-parameters "^6.24.1" + babel-plugin-transform-es2015-shorthand-properties "^6.24.1" babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.24.1" babel-plugin-transform-es2015-template-literals "^6.22.0" babel-plugin-transform-es2015-typeof-symbol "^6.22.0" - babel-plugin-transform-es2015-unicode-regex "^6.22.0" - babel-plugin-transform-regenerator "^6.22.0" + babel-plugin-transform-es2015-unicode-regex "^6.24.1" + babel-plugin-transform-regenerator "^6.24.1" -babel-register@^6.24.0: - version "6.24.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.0.tgz#5e89f8463ba9970356d02eb07dabe3308b080cfd" +babel-register@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f" dependencies: - babel-core "^6.24.0" + babel-core "^6.24.1" babel-runtime "^6.22.0" core-js "^2.4.0" home-or-tmp "^2.0.0" @@ -709,33 +709,33 @@ babel-runtime@^6.18.0, babel-runtime@^6.20.0, babel-runtime@^6.22.0, babel-runti core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.22.0, babel-template@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638" +babel-template@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" dependencies: babel-runtime "^6.22.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" babylon "^6.11.0" lodash "^4.2.0" -babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: - version "6.23.1" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48" +babel-traverse@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" dependencies: babel-code-frame "^6.22.0" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" babylon "^6.15.0" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf" +babel-types@^6.19.0, babel-types@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" dependencies: babel-runtime "^6.22.0" esutils "^2.0.2" @@ -885,8 +885,8 @@ bootstrap@^3.3.7: resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71" brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + version "1.1.7" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" dependencies: balanced-match "^0.4.1" concat-map "0.0.1" @@ -911,16 +911,16 @@ browserify-zlib@^0.1.4: dependencies: pako "~0.2.0" -browserslist@^1.0.1, browserslist@^1.5.2, browserslist@^1.7.6: +browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: version "1.7.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" dependencies: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" -buble@^0.14.0: - version "0.14.3" - resolved "https://registry.yarnpkg.com/buble/-/buble-0.14.3.tgz#678cb1fc24dc90e5ada047deaa5ddc6f6da8983e" +buble@^0.15.0: + version "0.15.2" + resolved "https://registry.yarnpkg.com/buble/-/buble-0.15.2.tgz#547fc47483f8e5e8176d82aa5ebccb183b02d613" dependencies: acorn "^3.3.0" acorn-jsx "^3.0.1" @@ -930,7 +930,7 @@ buble@^0.14.0: minimist "^1.2.0" os-homedir "^1.0.1" -buffer-shims@^1.0.0: +buffer-shims@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" @@ -955,8 +955,8 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" bunyan@^1.8.3: - version "1.8.9" - resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.9.tgz#2c7c9d422ea64ee2465d52b4decd72de0657401a" + version "1.8.10" + resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.10.tgz#201fedd26c7080b632f416072f53a90b9a52981c" optionalDependencies: dtrace-provider "~0.8" moment "^2.10.6" @@ -1016,17 +1016,17 @@ camelize@1.0.0: resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" caniuse-api@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.5.3.tgz#5018e674b51c393e4d50614275dc017e27c4a2a2" + version "1.6.1" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" dependencies: - browserslist "^1.0.1" - caniuse-db "^1.0.30000346" - lodash.memoize "^4.1.0" - lodash.uniq "^4.3.0" + browserslist "^1.3.6" + caniuse-db "^1.0.30000529" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" -caniuse-db@^1.0.30000346, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000640" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000640.tgz#7b7fd3cf13c0d9d41f8754b577b202113e2be7ca" +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: + version "1.0.30000650" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000650.tgz#615f564d367533d32b82d72ada09661e75386bab" caseless@~0.11.0: version "0.11.0" @@ -1122,8 +1122,8 @@ clap@^1.0.9: chalk "^1.1.3" clean-css@4.0.x: - version "4.0.10" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.0.10.tgz#6be448d6ba8c767654ebe11f158b97a887cb713f" + version "4.0.11" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.0.11.tgz#a6d88bffb399420b24298db49d99a1ed067534a8" dependencies: source-map "0.5.x" @@ -1206,15 +1206,15 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" "codemirror@git+https://github.com/hackmdio/CodeMirror.git": - version "5.21.1" - resolved "git+https://github.com/hackmdio/CodeMirror.git#6e6b29c967336bbdcec3dffd11ca776e173e563e" + version "5.25.1" + resolved "git+https://github.com/hackmdio/CodeMirror.git#a56213a385e22797e85d406cfbedc42617579354" dependencies: blint "^0.5.1" node-static "0.6.0" phantomjs-prebuilt "^2.1.12" - rollup "^0.34.10" - rollup-plugin-buble "^0.14.0" - rollup-watch "^2.5.0" + rollup "^0.41.0" + rollup-plugin-buble "^0.15.0" + rollup-watch "^3.2.0" uglify-js "^2.8.15" collapse-whitespace@1.1.2: @@ -1297,10 +1297,10 @@ component-inherit@0.0.3: resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" compressible@~2.0.8: - version "2.0.9" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.9.tgz#6daab4e2b599c2770dd9e21e7a891b1c5a755425" + version "2.0.10" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.10.tgz#feda1c7f7617912732b29bf8cf26252a20b9eecd" dependencies: - mime-db ">= 1.24.0 < 2" + mime-db ">= 1.27.0 < 2" compression@^1.6.2: version "1.6.2" @@ -1393,8 +1393,8 @@ content-type@~1.0.2: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" convert-source-map@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.4.0.tgz#e3dad195bf61bfe13a7a3c73e9876ec14a0268f3" + version "1.5.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" cookie-parser@1.4.3: version "1.4.3" @@ -1652,9 +1652,9 @@ debug-log@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" -debug@*, debug@2.6.1, debug@^2.1.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" +debug@*, debug@2.6.3, debug@^2.1.1, debug@^2.2.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" dependencies: ms "0.7.2" @@ -1674,9 +1674,9 @@ debug@2.3.3: dependencies: ms "0.7.2" -debug@2.6.3, debug@^2.2.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" +debug@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" dependencies: ms "0.7.2" @@ -1692,6 +1692,10 @@ deep-extend@~0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" +deep-freeze@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" + deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -1921,8 +1925,8 @@ ejs@^2.5.5: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88" electron-to-chromium@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.2.8.tgz#22c2e6200d350da27d6050db7e3f6f85d18cf4ed" + version "1.3.3" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e" emojify.js@~1.1.0: version "1.1.0" @@ -2175,10 +2179,10 @@ eslint@~3.18.0: user-home "^2.0.0" espree@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.0.tgz#41656fa5628e042878025ef467e78f125cb86e1d" + version "3.4.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.1.tgz#28a83ab4aaed71ed8fe0f5efe61b76a05c13c4d2" dependencies: - acorn "4.0.4" + acorn "^5.0.1" acorn-jsx "^3.0.0" esprima@^2.6.0, esprima@^2.7.1: @@ -2291,17 +2295,17 @@ expose-loader@^0.7.1: resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.3.tgz#35fbd3659789e4faa81f59de8b7e9fc39e466d51" express-session@^1.14.2: - version "1.15.1" - resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.1.tgz#9abba15971beea7ad98da5a4d25ed92ba4a2984e" + version "1.15.2" + resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.2.tgz#d98516443a4ccb8688e1725ae584c02daa4093d4" dependencies: cookie "0.3.1" cookie-signature "1.0.6" crc "3.4.4" - debug "2.6.1" + debug "2.6.3" depd "~1.1.0" on-headers "~1.0.1" parseurl "~1.3.1" - uid-safe "~2.1.3" + uid-safe "~2.1.4" utils-merge "1.0.0" express@>=4.14: @@ -2594,8 +2598,8 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" flowchart.js@^1.6.4: - version "1.6.5" - resolved "https://registry.yarnpkg.com/flowchart.js/-/flowchart.js-1.6.5.tgz#877aa913bd6a8ea99d98aa4613e83dc3b9f943de" + version "1.6.6" + resolved "https://registry.yarnpkg.com/flowchart.js/-/flowchart.js-1.6.6.tgz#2592aa881325614eefd681c00a40cca7561e85ec" dependencies: raphael "2.2.7" @@ -2622,8 +2626,8 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" form-data@~2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" @@ -2747,6 +2751,10 @@ generic-pool@2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.2.tgz#886bc5bf0beb7db96e81bcbba078818de5a62683" +generic-pool@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-2.4.3.tgz#780c36f69dfad05a5a045dd37be7adca11a4f6ff" + get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" @@ -2870,8 +2878,8 @@ global-prefix@^0.1.4: which "^1.2.12" globals@^9.0.0, globals@^9.14.0: - version "9.16.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.16.0.tgz#63e903658171ec2d9f51b1d31de5e2b8dc01fb80" + version "9.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286" globby@^5.0.0: version "5.0.0" @@ -2946,19 +2954,19 @@ grunt-contrib-jshint@: jshint "~2.9.4" grunt-contrib-qunit@: - version "1.3.0" - resolved "https://registry.yarnpkg.com/grunt-contrib-qunit/-/grunt-contrib-qunit-1.3.0.tgz#9dac628cfd4ec815998633db73b52bdb3ddbc99e" + version "2.0.0" + resolved "https://registry.yarnpkg.com/grunt-contrib-qunit/-/grunt-contrib-qunit-2.0.0.tgz#54a51b4b2c84fee62c3b7e00145c928d1ec2b7ec" dependencies: grunt-lib-phantomjs "^1.0.0" grunt-contrib-uglify@: - version "2.2.0" - resolved "https://registry.yarnpkg.com/grunt-contrib-uglify/-/grunt-contrib-uglify-2.2.0.tgz#9e67c2469b4774daa3345840511d51fc4fb34f19" + version "2.3.0" + resolved "https://registry.yarnpkg.com/grunt-contrib-uglify/-/grunt-contrib-uglify-2.3.0.tgz#b3d0260ebdd6cefa12ff2f8e9e1e259f7de4216f" dependencies: chalk "^1.0.0" maxmin "^1.1.0" object.assign "^4.0.4" - uglify-js "~2.8.3" + uglify-js "~2.8.21" uri-path "^1.0.0" grunt-known-options@~1.1.0: @@ -3217,8 +3225,8 @@ html-encoding-sniffer@^1.0.1: whatwg-encoding "^1.0.1" html-minifier@^3.2.3: - version "3.4.2" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.4.2.tgz#31896baaf735c1d95f7a0b7291f9dc36c0720752" + version "3.4.3" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.4.3.tgz#eb3a7297c804611f470454eeebe0aacc427e424a" dependencies: camel-case "3.0.x" clean-css "4.0.x" @@ -3227,7 +3235,7 @@ html-minifier@^3.2.3: ncname "1.0.x" param-case "2.1.x" relateurl "0.2.x" - uglify-js "2.8.x" + uglify-js "~2.8.22" html-webpack-plugin@^2.25.0: version "2.28.0" @@ -3331,8 +3339,8 @@ ienoopen@1.0.0: resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.0.0.tgz#346a428f474aac8f50cf3784ea2d0f16f62bda6b" ignore@^3.0.9, ignore@^3.2.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.6.tgz#26e8da0644be0bb4cb39516f6c79f0e0f4ffe48c" + version "3.2.7" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.7.tgz#4810ca5f1d8eca5595213a34b94f2eb4ed926bbd" ignorefs@^1.0.0: version "1.2.0" @@ -3351,7 +3359,7 @@ image-size@~0.5.0: "imgur@git+https://github.com/hackmdio/node-imgur.git": version "0.1.7" - resolved "git+https://github.com/hackmdio/node-imgur.git#91b86ccea46f27a57d8a164661f629603b46fbef" + resolved "git+https://github.com/hackmdio/node-imgur.git#c0fc609da4056f3bb66f3877057c789cb90e4b42" dependencies: commander "^2.3.0" glob "^4.0.5" @@ -3433,8 +3441,8 @@ interpret@^0.6.4: resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" interpret@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" + version "1.0.2" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.2.tgz#f4f623f0bb7122f15f5717c8e254b8161b5c5b2d" invariant@^2.2.0: version "2.2.2" @@ -3450,9 +3458,9 @@ ionicons@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ionicons/-/ionicons-2.0.1.tgz#ca398113293ea870244f538f0aabbd4b5b209a3e" -ipaddr.js@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.2.0.tgz#8aba49c9192799585bdd643e0ccb50e8ae777ba4" +ipaddr.js@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec" is-absolute-url@^2.0.0: version "2.1.0" @@ -3706,8 +3714,8 @@ js-beautify@^1.6.11: nopt "~3.0.1" js-cookie@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.3.tgz#48071625217ac9ecfab8c343a13d42ec09ff0526" + version "2.1.4" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.4.tgz#da4ec503866f149d164cf25f579ef31015025d8d" js-sequence-diagrams@^1000000.0.6: version "1000000.0.6" @@ -3729,8 +3737,8 @@ js-url@^2.3.0: grunt-contrib-uglify "" js-yaml@^3.5.1, js-yaml@^3.7.0: - version "3.8.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.2.tgz#02d3e2c0f6beab20248d412c352203827d786721" + version "3.8.3" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766" dependencies: argparse "^1.0.7" esprima "^3.1.1" @@ -4129,7 +4137,7 @@ lodash.mapvalues@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" -lodash.memoize@^4.1.0: +lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4178,7 +4186,7 @@ lodash.templatesettings@^3.0.0: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" -lodash.uniq@^4.3.0: +lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -4202,10 +4210,6 @@ lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" -lodash@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.5.0.tgz#19bb3f4d51278f0b8c818ed145c74ecf9fe40e6d" - longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -4462,10 +4466,10 @@ meta-marked@^0.4.2: marked "~0.3.6" method-override@^2.3.7: - version "2.3.7" - resolved "https://registry.yarnpkg.com/method-override/-/method-override-2.3.7.tgz#8e1d47ac480fb0cd8777083f11c896901166b2e5" + version "2.3.8" + resolved "https://registry.yarnpkg.com/method-override/-/method-override-2.3.8.tgz#178234bf4bab869f89df9444b06fc6147b44828c" dependencies: - debug "2.3.3" + debug "2.6.3" methods "~1.1.2" parseurl "~1.3.1" vary "~1.1.0" @@ -4492,19 +4496,15 @@ micromatch@^2.1.5, micromatch@^2.3.7: parse-glob "^3.0.4" regex-cache "^0.4.2" -"mime-db@>= 1.24.0 < 2": +"mime-db@>= 1.27.0 < 2", mime-db@~1.27.0: version "1.27.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" -mime-db@~1.26.0: - version "1.26.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" - -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7: - version "2.1.14" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: + version "2.1.15" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" dependencies: - mime-db "~1.26.0" + mime-db "~1.27.0" mime@1.3.4, mime@1.3.x, mime@^1.2.11: version "1.3.4" @@ -4554,12 +4554,12 @@ mkdirp@0.5.0: minimist "0.0.8" moment-timezone@^0.5.4: - version "0.5.11" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.11.tgz#9b76c03d8ef514c7e4249a7bbce649eed39ef29f" + version "0.5.13" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.13.tgz#99ce5c7d827262eb0f1f702044177f60745d7b90" dependencies: - moment ">= 2.6.0" + moment ">= 2.9.0" -"moment@>= 2.6.0", moment@^2.10.6, moment@^2.13.0, moment@^2.17.1, moment@^2.8.3, moment@^2.9.0: +"moment@>= 2.9.0", moment@^2.10.6, moment@^2.13.0, moment@^2.17.1, moment@^2.8.3, moment@^2.9.0: version "2.18.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" @@ -4616,8 +4616,8 @@ mysql@^2.12.0: sqlstring "2.2.0" nan@^2.0.8, nan@^2.3.0, nan@^2.3.3: - version "2.5.1" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" + version "2.6.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" nan@~2.4.0: version "2.4.0" @@ -4745,8 +4745,10 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: validate-npm-package-license "^3.0.1" normalize-path@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" normalize-range@^0.1.2: version "0.1.2" @@ -5184,10 +5186,10 @@ pg-hstore@^2.3.2: underscore "^1.7.0" pg-pool@1.*: - version "1.6.0" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.6.0.tgz#2e300199927b6d7db6be71e2e3435dddddf07b41" + version "1.7.0" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-1.7.0.tgz#f795f4a39439fa0ffbdd418052e40c51e8782369" dependencies: - generic-pool "2.4.2" + generic-pool "2.4.3" object-assign "4.1.0" pg-types@1.*: @@ -5559,8 +5561,8 @@ pretty-bytes@^1.0.0: meow "^3.1.0" pretty-error@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.0.3.tgz#bed3d816a008e76da617cde8216f4b778849b5d9" + version "2.1.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.0.tgz#87f4e9d706a24c87d6cbee9fabec001fcf8c75d8" dependencies: renderkid "^2.0.1" utila "~0.4" @@ -5602,11 +5604,11 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" proxy-addr@~1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.3.tgz#dc97502f5722e888467b3fa2297a7b1ff47df074" + version "1.1.4" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" dependencies: forwarded "~0.1.0" - ipaddr.js "1.2.0" + ipaddr.js "1.3.0" prr@~0.0.0: version "0.0.0" @@ -5705,8 +5707,8 @@ raw-loader@~0.5.1: resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" rc@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea" + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" dependencies: deep-extend "~0.4.0" ini "~1.3.0" @@ -5746,16 +5748,16 @@ readable-stream@1.1, readable-stream@1.1.14, readable-stream@~1.1.9: isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.0, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2: - version "2.2.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.6.tgz#8b43aed76e71483938d12a8d46c6cf1a00b1f816" +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6: + version "2.2.9" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" dependencies: - buffer-shims "^1.0.0" + buffer-shims "~1.0.0" core-util-is "~1.0.0" inherits "~2.0.1" isarray "~1.0.0" process-nextick-args "~1.0.6" - string_decoder "~0.10.x" + string_decoder "~1.0.0" util-deprecate "~1.0.1" readable-stream@~2.0.0: @@ -5829,9 +5831,9 @@ regenerator-runtime@^0.10.0: version "0.10.3" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e" -regenerator-transform@0.9.8: - version "0.9.8" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.8.tgz#0f88bb2bc03932ddb7b6b7312e68078f01026d6c" +regenerator-transform@0.9.11: + version "0.9.11" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" dependencies: babel-runtime "^6.18.0" babel-types "^6.19.0" @@ -5881,6 +5883,10 @@ remarkable@^1.6.0: argparse "~0.1.15" autolinker "~0.15.0" +remove-trailing-separator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + renderkid@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.1.tgz#898cabfc8bede4b7b91135a3ffd323e58c0db319" @@ -5975,6 +5981,10 @@ require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" +require-relative@0.8.7: + version "0.8.7" + resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" + require-uncached@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" @@ -6049,11 +6059,11 @@ ripemd160@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce" -rollup-plugin-buble@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-buble/-/rollup-plugin-buble-0.14.0.tgz#3726db55fef9b9cd37cebed559cbbd4b9b2e5bc6" +rollup-plugin-buble@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-buble/-/rollup-plugin-buble-0.15.0.tgz#83c3e89c7fd2266c7918f41ba3980313519c7fd0" dependencies: - buble "^0.14.0" + buble "^0.15.0" rollup-pluginutils "^1.5.0" rollup-pluginutils@^1.5.0: @@ -6063,15 +6073,15 @@ rollup-pluginutils@^1.5.0: estree-walker "^0.2.1" minimatch "^3.0.2" -rollup-watch@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/rollup-watch/-/rollup-watch-2.5.0.tgz#852d660ddecc51696890aa8c22e95ed4558cc5f7" +rollup-watch@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/rollup-watch/-/rollup-watch-3.2.2.tgz#5e574232e9ef36da9177f46946d8080cb267354b" dependencies: - semver "^5.1.0" + require-relative "0.8.7" -rollup@^0.34.10: - version "0.34.13" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.34.13.tgz#a211cdde31f96cb39e7cb4e35becb15ddc3efa19" +rollup@^0.41.0: + version "0.41.6" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.41.6.tgz#e0d05497877a398c104d816d2733a718a7a94e2a" dependencies: source-map-support "^0.4.0" @@ -6103,13 +6113,9 @@ safefs@^3.1.2: dependencies: graceful-fs "*" -sax@1.1.5, sax@>=0.6.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.5.tgz#1da50a8d00cdecd59405659f5ff85349fe773743" - -sax@^1.2.1, sax@~1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" +sax@1.2.1, sax@>=0.6.0, sax@^1.2.1, sax@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" scandirectory@^2.5.0: version "2.5.0" @@ -6184,8 +6190,8 @@ send@0.15.1: statuses "~1.3.1" sequelize-cli@^2.5.1: - version "2.6.0" - resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-2.6.0.tgz#8fb7b1d4aa40a4ff26d33fd72f3d0223d9506baa" + version "2.7.0" + resolved "https://registry.yarnpkg.com/sequelize-cli/-/sequelize-cli-2.7.0.tgz#122c26bf46609001fbc158404529f795f3950ea2" dependencies: bluebird "^3.5.0" cli-color "~1.2.0" @@ -6201,8 +6207,8 @@ sequelize-cli@^2.5.1: yargs "^7.0.1" sequelize@^3.28.0: - version "3.30.2" - resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-3.30.2.tgz#8fb5a5769a01777451593924092f9d3061eb935f" + version "3.30.4" + resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-3.30.4.tgz#bda2df1e31854b099e4149a111e9fc0a5ca1d1a4" dependencies: bluebird "^3.3.4" depd "^1.1.0" @@ -6516,12 +6522,12 @@ stream-from-to@^1.4.2: series-stream "^1.0.1" stream-http@^2.3.1: - version "2.6.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.3.tgz#4c3ddbf9635968ea2cfd4e48d43de5def2625ac3" + version "2.7.0" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.0.tgz#cec1f4e3b494bc4a81b451808970f8b20b4ed5f6" dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" - readable-stream "^2.1.0" + readable-stream "^2.2.6" to-arraybuffer "^1.0.0" xtend "^4.0.0" @@ -6560,6 +6566,12 @@ string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" +string_decoder@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667" + dependencies: + buffer-shims "~1.0.0" + stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -6761,8 +6773,8 @@ timers-browserify@^2.0.2: setimmediate "^1.0.4" timers-ext@0.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.1.tgz#d6422f1ebd67772355f46c93f25e3933992c8b08" + version "0.1.2" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.2.tgz#61cc47a76c1abd3195f14527f978d58ae94c5204" dependencies: es5-ext "~0.10.14" next-tick "1" @@ -6861,11 +6873,11 @@ type-check@~0.3.2: prelude-ls "~1.1.2" type-is@~1.6.14: - version "1.6.14" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2" + version "1.6.15" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" dependencies: media-typer "0.3.0" - mime-types "~2.1.13" + mime-types "~2.1.15" typechecker@^2.0.8: version "2.1.0" @@ -6889,9 +6901,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" -uglify-js@2.8.x, uglify-js@^2.6, uglify-js@^2.6.2, uglify-js@^2.8.15, uglify-js@~2.8.3: - version "2.8.15" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.15.tgz#835dd4cd5872554756e6874508d0d0561704d94d" +uglify-js@^2.6, uglify-js@^2.6.2, uglify-js@^2.8.15, uglify-js@~2.8.21, uglify-js@~2.8.22: + version "2.8.22" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.22.tgz#d54934778a8da14903fa29a326fb24c0ab51a1a0" dependencies: source-map "~0.5.1" yargs "~3.10.0" @@ -6915,7 +6927,7 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" -uid-safe@~2.1.3: +uid-safe@~2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.4.tgz#3ad6f38368c6d4c8c75ec17623fb79aa1d071d81" dependencies: @@ -7046,17 +7058,17 @@ utils-merge@1.0.0, utils-merge@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" -uuid@3.0.0, uuid@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728" +uuid@3.0.1, uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" uws@~0.14.1: version "0.14.1" resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.1.tgz#89b8eb7bced272c65621472e878c57f4e0e00640" v8flags@^2.0.10, v8flags@^2.0.2: - version "2.0.11" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" + version "2.0.12" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.12.tgz#73235d9f7176f8e8833fb286795445f7938d84e5" dependencies: user-home "^1.1.1" @@ -7243,8 +7255,8 @@ whatwg-encoding@^1.0.1: iconv-lite "0.4.13" whatwg-url@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.6.0.tgz#ef98da442273be04cf9632e176f257d2395a1ae4" + version "4.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.7.0.tgz#202035ac1955b087cdd20fa8b58ded3ab1cd2af5" dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" @@ -7340,18 +7352,18 @@ xml-name-validator@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" -xml2js@0.4.15: - version "0.4.15" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.15.tgz#95cd03ff2dd144ec28bc6273bf2b2890c581ad0c" +xml2js@0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.17.tgz#17be93eaae3f3b779359c795b419705a8817e868" dependencies: sax ">=0.6.0" - xmlbuilder ">=2.4.6" + xmlbuilder "^4.1.0" -xmlbuilder@2.6.2, xmlbuilder@>=2.4.6: - version "2.6.2" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-2.6.2.tgz#f916f6d10d45dc171b1be2e6e673fb6e0cc35d0a" +xmlbuilder@4.2.1, xmlbuilder@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.2.1.tgz#aa58a3041a066f90eaa16c2f5389ff19f3f461a5" dependencies: - lodash "~3.5.0" + lodash "^4.0.0" xmldom@0.1.x: version "0.1.27" |